Publish expanded LumynaX product platform package
Browse files- PYPI_RELEASE.md +1 -1
- README.md +19 -2
- marama_route/cli.py +326 -81
- marama_route/platform.py +117 -3
- pyproject.toml +1 -1
PYPI_RELEASE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# PyPI Release: LumynaX MaramaRoute
|
| 2 |
|
| 3 |
Package: `lumynax-marama-route`
|
| 4 |
-
Version: `0.
|
| 5 |
|
| 6 |
## Local Build
|
| 7 |
|
|
|
|
| 1 |
# PyPI Release: LumynaX MaramaRoute
|
| 2 |
|
| 3 |
Package: `lumynax-marama-route`
|
| 4 |
+
Version: `0.8.0`
|
| 5 |
|
| 6 |
## Local Build
|
| 7 |
|
README.md
CHANGED
|
@@ -95,8 +95,8 @@ MaramaRoute chat
|
|
| 95 |
# Or jump straight to a model by id or search fragment.
|
| 96 |
MaramaRoute chat qwen25-05b
|
| 97 |
|
| 98 |
-
# Create local config, default alias,
|
| 99 |
-
MaramaRoute
|
| 100 |
MaramaRoute agent doctor --model qwen25-7b
|
| 101 |
|
| 102 |
# Inspect registry categories before choosing a model.
|
|
@@ -105,6 +105,7 @@ MaramaRoute categories
|
|
| 105 |
# Install local runtimes when you want direct generation.
|
| 106 |
python -m pip install llama-cpp-python
|
| 107 |
python -m pip install torch
|
|
|
|
| 108 |
|
| 109 |
# Estimate, pull, verify, and chat.
|
| 110 |
MaramaRoute pull qwen25-05b --estimate --remote-sizes
|
|
@@ -209,8 +210,15 @@ Use these commands before putting a machine or workspace into regular use:
|
|
| 209 |
# Install, registry, cache, HF tooling, and local runtime readiness.
|
| 210 |
MaramaRoute doctor --hardware
|
| 211 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
# Workspace bridge config and optional gateway health probe.
|
| 213 |
MaramaRoute agent doctor --target claude-code --model qwen25-7b
|
|
|
|
|
|
|
|
|
|
| 214 |
MaramaRoute agent doctor --target hpe --model qwen25-7b --base-url http://127.0.0.1:8787/v1
|
| 215 |
|
| 216 |
# Exact remote size planning, local hash verification, and registry drift check.
|
|
@@ -445,9 +453,18 @@ MaramaRoute matrix
|
|
| 445 |
### Generate agent and HPE/HPC helper configs
|
| 446 |
|
| 447 |
```bash
|
|
|
|
|
|
|
|
|
|
| 448 |
# Command bridge JSON for coding-agent workspaces.
|
| 449 |
MaramaRoute agent-config --target claude-code --model qwen25-7b
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
MaramaRoute agent-init --target claude-code --model qwen25-7b
|
|
|
|
| 451 |
MaramaRoute agent init --target claude-code --model qwen25-7b
|
| 452 |
MaramaRoute agent doctor --target claude-code --model qwen25-7b
|
| 453 |
|
|
|
|
| 95 |
# Or jump straight to a model by id or search fragment.
|
| 96 |
MaramaRoute chat qwen25-05b
|
| 97 |
|
| 98 |
+
# Create local config, default alias, coding-agent bridge files, and HPE scaffold.
|
| 99 |
+
MaramaRoute setup --all-targets --hpe
|
| 100 |
MaramaRoute agent doctor --model qwen25-7b
|
| 101 |
|
| 102 |
# Inspect registry categories before choosing a model.
|
|
|
|
| 105 |
# Install local runtimes when you want direct generation.
|
| 106 |
python -m pip install llama-cpp-python
|
| 107 |
python -m pip install torch
|
| 108 |
+
python -m pip install sentencepiece tiktoken tokenizers
|
| 109 |
|
| 110 |
# Estimate, pull, verify, and chat.
|
| 111 |
MaramaRoute pull qwen25-05b --estimate --remote-sizes
|
|
|
|
| 210 |
# Install, registry, cache, HF tooling, and local runtime readiness.
|
| 211 |
MaramaRoute doctor --hardware
|
| 212 |
|
| 213 |
+
# One-shot production bootstrap: local config, aliases, agent bridge files, and optional HPE bundle.
|
| 214 |
+
MaramaRoute setup qwen25-05b --all-targets --hpe
|
| 215 |
+
MaramaRoute setup qwen25-7b --target claude-code,codex,continue,opencode,litellm,tabby --hpe --backend vllm
|
| 216 |
+
|
| 217 |
# Workspace bridge config and optional gateway health probe.
|
| 218 |
MaramaRoute agent doctor --target claude-code --model qwen25-7b
|
| 219 |
+
MaramaRoute agent doctor --target codex --model qwen25-7b
|
| 220 |
+
MaramaRoute agent doctor --target continue --model qwen25-7b
|
| 221 |
+
MaramaRoute agent doctor --target litellm --model qwen25-7b
|
| 222 |
MaramaRoute agent doctor --target hpe --model qwen25-7b --base-url http://127.0.0.1:8787/v1
|
| 223 |
|
| 224 |
# Exact remote size planning, local hash verification, and registry drift check.
|
|
|
|
| 453 |
### Generate agent and HPE/HPC helper configs
|
| 454 |
|
| 455 |
```bash
|
| 456 |
+
# One command writes MaramaRoute config, aliases, agent bridge files, and HPE scaffold.
|
| 457 |
+
MaramaRoute setup qwen25-7b --all-targets --hpe --backend vllm
|
| 458 |
+
|
| 459 |
# Command bridge JSON for coding-agent workspaces.
|
| 460 |
MaramaRoute agent-config --target claude-code --model qwen25-7b
|
| 461 |
+
MaramaRoute agent-config --target codex --model qwen25-7b
|
| 462 |
+
MaramaRoute agent-config --target continue --model qwen25-7b
|
| 463 |
+
MaramaRoute agent-config --target opencode --model qwen25-7b
|
| 464 |
+
MaramaRoute agent-config --target litellm --model qwen25-7b
|
| 465 |
+
MaramaRoute agent-config --target tabby --model qwen25-7b
|
| 466 |
MaramaRoute agent-init --target claude-code --model qwen25-7b
|
| 467 |
+
MaramaRoute agent-init --target codex --model qwen25-7b
|
| 468 |
MaramaRoute agent init --target claude-code --model qwen25-7b
|
| 469 |
MaramaRoute agent doctor --target claude-code --model qwen25-7b
|
| 470 |
|
marama_route/cli.py
CHANGED
|
@@ -56,6 +56,7 @@ from .platform import (
|
|
| 56 |
build_registry_analytics,
|
| 57 |
catalog_models,
|
| 58 |
compare_models,
|
|
|
|
| 59 |
recommend_model,
|
| 60 |
render_hpe_apptainer_definition,
|
| 61 |
render_hpe_gateway_config,
|
|
@@ -105,6 +106,9 @@ _DEPLOYMENT_TARGET_LABELS = {
|
|
| 105 |
"nvidia_nim": "NVIDIA NIM",
|
| 106 |
"nvidia_nemo": "NVIDIA NeMo/NEM",
|
| 107 |
}
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
|
| 110 |
class _ExitConversation(Exception):
|
|
@@ -332,28 +336,64 @@ def _doctor(args: argparse.Namespace) -> int:
|
|
| 332 |
"path": str(cache_root),
|
| 333 |
"pulled_count": pulled["count"],
|
| 334 |
},
|
|
|
|
| 335 |
"huggingface_hub": {
|
| 336 |
"ok": importlib.util.find_spec("huggingface_hub") is not None,
|
| 337 |
"hf_cli": shutil.which("hf") or shutil.which("huggingface-cli") or "",
|
| 338 |
"token_env_present": any(os.getenv(name) for name in _HF_TOKEN_ENV_NAMES),
|
|
|
|
| 339 |
},
|
| 340 |
"llama_cpp": {
|
| 341 |
"ok": importlib.util.find_spec("llama_cpp") is not None,
|
| 342 |
"needed_for": "local GGUF chat and run",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 343 |
},
|
| 344 |
}
|
| 345 |
if args.online:
|
| 346 |
checks["huggingface_hub"]["whoami"] = _hf_whoami_status()
|
|
|
|
| 347 |
result = {
|
| 348 |
-
"ok":
|
| 349 |
"product": "LumynaX MaramaRoute",
|
| 350 |
"checks": checks,
|
|
|
|
| 351 |
"hardware": hardware,
|
| 352 |
"next_commands": {
|
|
|
|
| 353 |
"install_runtime": "python -m pip install llama-cpp-python",
|
|
|
|
| 354 |
"choose_model": "MaramaRoute chat",
|
| 355 |
"pull_small_model": "MaramaRoute pull qwen25-05b",
|
| 356 |
-
"run_gateway": "MaramaRoute serve --port 8787",
|
|
|
|
|
|
|
| 357 |
},
|
| 358 |
}
|
| 359 |
if args.json:
|
|
@@ -492,6 +532,231 @@ def _init_self_test(models: tuple[Any, ...], model: Any, args: argparse.Namespac
|
|
| 492 |
return {"enabled": True, "ok": not blocking, "checks": checks}
|
| 493 |
|
| 494 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 495 |
def _writable_dir(path: Path) -> dict[str, Any]:
|
| 496 |
try:
|
| 497 |
path.mkdir(parents=True, exist_ok=True)
|
|
@@ -651,7 +916,6 @@ def _agent_init(args: argparse.Namespace) -> int:
|
|
| 651 |
model_ref = args.model or args.model_option
|
| 652 |
model = _resolve_cli_model(models, model_ref) if model_ref else _recommended_model(models)
|
| 653 |
target_dir = args.output_dir.resolve()
|
| 654 |
-
target_dir.mkdir(parents=True, exist_ok=True)
|
| 655 |
config = build_agent_bridge_config(
|
| 656 |
models,
|
| 657 |
target=args.target,
|
|
@@ -661,28 +925,16 @@ def _agent_init(args: argparse.Namespace) -> int:
|
|
| 661 |
cache_dir=args.cache_dir,
|
| 662 |
model_id=model.model_id,
|
| 663 |
)
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
"Use MaramaRoute for LumynaX model selection, local chat, and local-live gateway calls.",
|
| 675 |
-
f"Default model: `{model.model_id}`",
|
| 676 |
-
f"Start gateway: `{config['commands']['start_gateway']}`",
|
| 677 |
-
f"Recommend model: `{config['commands']['recommend']}`",
|
| 678 |
-
"",
|
| 679 |
-
),
|
| 680 |
-
),
|
| 681 |
-
encoding="utf-8",
|
| 682 |
-
)
|
| 683 |
-
written.append(str(claude_path))
|
| 684 |
-
print(json.dumps({"ok": True, "target": args.target, "model_id": model.model_id, "written": written}, indent=2, sort_keys=True))
|
| 685 |
-
return 0
|
| 686 |
|
| 687 |
|
| 688 |
def _agent_doctor(args: argparse.Namespace) -> int:
|
|
@@ -701,7 +953,7 @@ def _agent_doctor(args: argparse.Namespace) -> int:
|
|
| 701 |
health = _gateway_health(args.base_url)
|
| 702 |
checks = {
|
| 703 |
"registry": {"ok": bool(models), "count": len(models), "path": str(_registry_path(args))},
|
| 704 |
-
"target": {"ok": config.get("target") in
|
| 705 |
"cache": {"ok": True, "path": str(args.cache_dir or default_cache_root())},
|
| 706 |
"gateway": health,
|
| 707 |
}
|
|
@@ -745,56 +997,22 @@ def _hpe(args: argparse.Namespace) -> int:
|
|
| 745 |
cache_dir="$MARAMA_ROUTE_CACHE",
|
| 746 |
)
|
| 747 |
if args.hpe_action == "init":
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
memory=args.memory,
|
| 764 |
-
time_limit=args.time,
|
| 765 |
-
partition=args.partition,
|
| 766 |
-
),
|
| 767 |
-
encoding="utf-8",
|
| 768 |
-
)
|
| 769 |
-
gateway = output_dir / "gateway.hpe.json"
|
| 770 |
-
gateway.write_text(json.dumps(gateway_config, indent=2, sort_keys=True), encoding="utf-8")
|
| 771 |
-
env = output_dir / "marama-route.env"
|
| 772 |
-
env.write_text(
|
| 773 |
-
"\n".join(
|
| 774 |
-
(
|
| 775 |
-
"MARAMA_ROUTE_CACHE=${SCRATCH:-$HOME}/marama-route/models",
|
| 776 |
-
f"MARAMA_ROUTE_PORT={args.port}",
|
| 777 |
-
f"MARAMA_BACKEND_PORT={args.backend_port}",
|
| 778 |
-
f"MARAMA_BACKEND_BASE_URL={args.backend_base_url or f'http://127.0.0.1:{args.backend_port}/v1'}",
|
| 779 |
-
f"MARAMA_BACKEND_MODEL={args.backend_model or model.model_id}",
|
| 780 |
-
f"MARAMA_BACKEND={args.backend}",
|
| 781 |
-
"",
|
| 782 |
-
),
|
| 783 |
-
),
|
| 784 |
-
encoding="utf-8",
|
| 785 |
)
|
| 786 |
-
definition = output_dir / "marama-route.def"
|
| 787 |
-
definition.write_text(render_hpe_apptainer_definition(backend=args.backend), encoding="utf-8")
|
| 788 |
-
readme = output_dir / "README.hpe.md"
|
| 789 |
-
readme.write_text(render_hpe_readme(model_id=model.model_id, port=args.port, backend=args.backend), encoding="utf-8")
|
| 790 |
-
result = {
|
| 791 |
-
"ok": True,
|
| 792 |
-
"written": [str(script), str(gateway), str(env), str(definition), str(readme)],
|
| 793 |
-
"model_id": model.model_id,
|
| 794 |
-
"repo_id": model.repo_id,
|
| 795 |
-
"backend": args.backend,
|
| 796 |
-
"gateway_config": gateway_config,
|
| 797 |
-
}
|
| 798 |
elif args.hpe_action == "submit":
|
| 799 |
script = output_dir / "marama-route.slurm"
|
| 800 |
script.write_text(
|
|
@@ -1892,7 +2110,7 @@ def _print_startup_guide(models: tuple[Any, ...], cache_dir: Path | None, *, ani
|
|
| 1892 |
"",
|
| 1893 |
"Start here: press Enter for hardware-suitable models, or type /help any time.",
|
| 1894 |
"Offline flow: /models -> choose -> /pull -> chat. Use /switch <text> to change.",
|
| 1895 |
-
"Production commands: doctor | verify --deep | serve --live-local | agent-init | hpe init",
|
| 1896 |
"MaramaRoute serve --live-local --port 8787",
|
| 1897 |
],
|
| 1898 |
)
|
|
@@ -2196,7 +2414,9 @@ def _print_doctor(result: dict[str, Any]) -> None:
|
|
| 2196 |
print(f"- {name}: {mark}{detail}")
|
| 2197 |
if not result["checks"]["llama_cpp"]["ok"]:
|
| 2198 |
print("Install local GGUF runtime: python -m pip install llama-cpp-python")
|
| 2199 |
-
|
|
|
|
|
|
|
| 2200 |
|
| 2201 |
|
| 2202 |
def _ensure_model_ready(
|
|
@@ -2403,12 +2623,37 @@ def build_parser() -> argparse.ArgumentParser:
|
|
| 2403 |
init.add_argument("--host", default="127.0.0.1")
|
| 2404 |
init.add_argument("--port", type=int, default=8787)
|
| 2405 |
init.add_argument("--live-local", action=argparse.BooleanOptionalAction, default=True)
|
| 2406 |
-
init.add_argument("--agent", default="", choices=
|
| 2407 |
init.add_argument("--hpe", action=argparse.BooleanOptionalAction, default=False)
|
| 2408 |
init.add_argument("--pull", action=argparse.BooleanOptionalAction, default=False)
|
| 2409 |
init.add_argument("--self-test", action=argparse.BooleanOptionalAction, default=True)
|
| 2410 |
init.set_defaults(handler=_init)
|
| 2411 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2412 |
hardware = subparsers.add_parser("hardware", help="Inspect local hardware and recommend runnable LumynaX models.")
|
| 2413 |
hardware.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
|
| 2414 |
hardware.add_argument("--cache-dir", type=Path, default=default_cache_root(), help="Local model cache directory.")
|
|
@@ -2505,7 +2750,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
| 2505 |
help="Emit command-bridge config for coding agents, local gateways, or HPE/Slurm jobs.",
|
| 2506 |
)
|
| 2507 |
agent.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
|
| 2508 |
-
agent.add_argument("--target", default="generic", choices=
|
| 2509 |
agent.add_argument("--model", default="", help="Default model id, repo id, or unique search fragment.")
|
| 2510 |
agent.add_argument("--base-url", default="http://127.0.0.1:8787/v1")
|
| 2511 |
agent.add_argument("--host", default="127.0.0.1")
|
|
@@ -2518,7 +2763,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
| 2518 |
agent_init.add_argument("model", nargs="?", help="Optional default model id, repo id, alias, or search fragment.")
|
| 2519 |
agent_init.add_argument("--model", dest="model_option", default="", help="Default model id, repo id, alias, or search fragment.")
|
| 2520 |
agent_init.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
|
| 2521 |
-
agent_init.add_argument("--target", default="claude-code", choices=
|
| 2522 |
agent_init.add_argument("--output-dir", type=Path, default=Path("."))
|
| 2523 |
agent_init.add_argument("--base-url", default="http://127.0.0.1:8787/v1")
|
| 2524 |
agent_init.add_argument("--host", default="127.0.0.1")
|
|
@@ -2528,7 +2773,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
| 2528 |
|
| 2529 |
agent_doctor = subparsers.add_parser("agent-doctor", help="Check agent bridge config and local gateway readiness.")
|
| 2530 |
agent_doctor.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
|
| 2531 |
-
agent_doctor.add_argument("--target", default="claude-code", choices=
|
| 2532 |
agent_doctor.add_argument("--model", default="", help="Default model id, repo id, alias, or search fragment.")
|
| 2533 |
agent_doctor.add_argument("--base-url", default="http://127.0.0.1:8787/v1")
|
| 2534 |
agent_doctor.add_argument("--host", default="127.0.0.1")
|
|
|
|
| 56 |
build_registry_analytics,
|
| 57 |
catalog_models,
|
| 58 |
compare_models,
|
| 59 |
+
normalize_agent_target,
|
| 60 |
recommend_model,
|
| 61 |
render_hpe_apptainer_definition,
|
| 62 |
render_hpe_gateway_config,
|
|
|
|
| 106 |
"nvidia_nim": "NVIDIA NIM",
|
| 107 |
"nvidia_nemo": "NVIDIA NeMo/NEM",
|
| 108 |
}
|
| 109 |
+
_AGENT_TARGET_CHOICES = ("generic", "claude-code", "codex", "continue", "opencode", "litellm", "tabby", "hpe", "hpe-slurm")
|
| 110 |
+
_AGENT_SETUP_DEFAULT_TARGETS = ("claude-code", "codex", "continue", "opencode", "litellm", "tabby")
|
| 111 |
+
_AGENT_CANONICAL_TARGETS = {"generic", "claude-code", "codex", "continue", "opencode", "litellm", "tabby", "hpe-slurm"}
|
| 112 |
|
| 113 |
|
| 114 |
class _ExitConversation(Exception):
|
|
|
|
| 336 |
"path": str(cache_root),
|
| 337 |
"pulled_count": pulled["count"],
|
| 338 |
},
|
| 339 |
+
"cache_writable": _writable_dir(cache_root),
|
| 340 |
"huggingface_hub": {
|
| 341 |
"ok": importlib.util.find_spec("huggingface_hub") is not None,
|
| 342 |
"hf_cli": shutil.which("hf") or shutil.which("huggingface-cli") or "",
|
| 343 |
"token_env_present": any(os.getenv(name) for name in _HF_TOKEN_ENV_NAMES),
|
| 344 |
+
"needed_for": "pull, update-registry, and private HF access",
|
| 345 |
},
|
| 346 |
"llama_cpp": {
|
| 347 |
"ok": importlib.util.find_spec("llama_cpp") is not None,
|
| 348 |
"needed_for": "local GGUF chat and run",
|
| 349 |
+
"severity": "warning",
|
| 350 |
+
},
|
| 351 |
+
"transformers": {
|
| 352 |
+
"ok": importlib.util.find_spec("transformers") is not None,
|
| 353 |
+
"needed_for": "offline Transformers text-generation snapshots",
|
| 354 |
+
"severity": "warning",
|
| 355 |
+
},
|
| 356 |
+
"tokenizer_extras": {
|
| 357 |
+
"ok": any(importlib.util.find_spec(name) is not None for name in ("sentencepiece", "tiktoken", "tokenizers")),
|
| 358 |
+
"needed_for": "slow-tokenizer conversion and some Transformers snapshots",
|
| 359 |
+
"severity": "warning",
|
| 360 |
+
},
|
| 361 |
+
"git": {
|
| 362 |
+
"ok": shutil.which("git") is not None,
|
| 363 |
+
"executable": shutil.which("git") or "",
|
| 364 |
+
"severity": "warning",
|
| 365 |
+
},
|
| 366 |
+
"node": {
|
| 367 |
+
"ok": shutil.which("node") is not None,
|
| 368 |
+
"executable": shutil.which("node") or "",
|
| 369 |
+
"needed_for": "npm wrapper usage",
|
| 370 |
+
"severity": "warning",
|
| 371 |
+
},
|
| 372 |
+
"npm": {
|
| 373 |
+
"ok": shutil.which("npm") is not None,
|
| 374 |
+
"executable": shutil.which("npm") or "",
|
| 375 |
+
"needed_for": "npm wrapper usage",
|
| 376 |
+
"severity": "warning",
|
| 377 |
},
|
| 378 |
}
|
| 379 |
if args.online:
|
| 380 |
checks["huggingface_hub"]["whoami"] = _hf_whoami_status()
|
| 381 |
+
blocking = [name for name, item in checks.items() if not item.get("ok") and item.get("severity") != "warning"]
|
| 382 |
result = {
|
| 383 |
+
"ok": not blocking,
|
| 384 |
"product": "LumynaX MaramaRoute",
|
| 385 |
"checks": checks,
|
| 386 |
+
"blocking_checks": blocking,
|
| 387 |
"hardware": hardware,
|
| 388 |
"next_commands": {
|
| 389 |
+
"setup": "MaramaRoute setup --all-targets --hpe",
|
| 390 |
"install_runtime": "python -m pip install llama-cpp-python",
|
| 391 |
+
"install_transformers_runtime": "python -m pip install transformers torch sentencepiece tiktoken tokenizers",
|
| 392 |
"choose_model": "MaramaRoute chat",
|
| 393 |
"pull_small_model": "MaramaRoute pull qwen25-05b",
|
| 394 |
+
"run_gateway": "MaramaRoute serve --live-local --port 8787",
|
| 395 |
+
"agent_doctor": "MaramaRoute agent doctor --target claude-code",
|
| 396 |
+
"hpe_init": "MaramaRoute hpe init qwen25-05b --backend auto",
|
| 397 |
},
|
| 398 |
}
|
| 399 |
if args.json:
|
|
|
|
| 532 |
return {"enabled": True, "ok": not blocking, "checks": checks}
|
| 533 |
|
| 534 |
|
| 535 |
+
def _setup(args: argparse.Namespace) -> int:
|
| 536 |
+
models = load_model_registry(_registry_path(args))
|
| 537 |
+
model = _resolve_cli_model(models, args.model) if args.model else _recommended_model(models)
|
| 538 |
+
state_root = args.state_dir or default_state_root()
|
| 539 |
+
state_root.mkdir(parents=True, exist_ok=True)
|
| 540 |
+
targets = _expand_agent_targets(args.target, all_targets=args.all_targets, include_hpe=args.hpe)
|
| 541 |
+
hardware = inspect_hardware(args.cache_dir)
|
| 542 |
+
self_test = _init_self_test(models, model, args)
|
| 543 |
+
base_url = f"http://{args.host}:{args.port}/v1"
|
| 544 |
+
config = {
|
| 545 |
+
"product": "LumynaX MaramaRoute",
|
| 546 |
+
"mode": "production_setup",
|
| 547 |
+
"version": _installed_version(),
|
| 548 |
+
"state_dir": str(state_root),
|
| 549 |
+
"cache_dir": str(args.cache_dir),
|
| 550 |
+
"default_model": model.model_id,
|
| 551 |
+
"default_repo": model.repo_id,
|
| 552 |
+
"default_runtime": model.runtime,
|
| 553 |
+
"hardware": hardware,
|
| 554 |
+
"agent_targets": targets,
|
| 555 |
+
"gateway": {
|
| 556 |
+
"host": args.host,
|
| 557 |
+
"port": args.port,
|
| 558 |
+
"base_url": base_url,
|
| 559 |
+
"live_local": True,
|
| 560 |
+
},
|
| 561 |
+
"next_commands": {
|
| 562 |
+
"doctor": "MaramaRoute doctor --hardware",
|
| 563 |
+
"pull": f"MaramaRoute pull {model.model_id}",
|
| 564 |
+
"chat": f"MaramaRoute chat {model.model_id}",
|
| 565 |
+
"serve": f"MaramaRoute serve --host {args.host} --port {args.port} --live-local",
|
| 566 |
+
"agent_doctor": "MaramaRoute agent doctor --target claude-code",
|
| 567 |
+
"hpe": f"MaramaRoute hpe init {model.model_id} --backend {args.backend}",
|
| 568 |
+
"compat_vllm": "MaramaRoute compat --target vllm --status usable",
|
| 569 |
+
"compat_nim": "MaramaRoute compat --target nim --status usable",
|
| 570 |
+
"compat_nemo": "MaramaRoute compat --target nemo --status pathway",
|
| 571 |
+
},
|
| 572 |
+
"self_test": self_test,
|
| 573 |
+
}
|
| 574 |
+
config_path = state_root / "marama-route.json"
|
| 575 |
+
config_path.write_text(json.dumps(config, indent=2, sort_keys=True), encoding="utf-8")
|
| 576 |
+
set_alias("default", model.model_id, state_root)
|
| 577 |
+
set_favorite(model.model_id, state_root)
|
| 578 |
+
|
| 579 |
+
artifacts: dict[str, Any] = {
|
| 580 |
+
"config": str(config_path),
|
| 581 |
+
"state_dir": str(state_root),
|
| 582 |
+
"aliases": str(state_root / "aliases.json"),
|
| 583 |
+
}
|
| 584 |
+
agent_artifacts: dict[str, Any] = {}
|
| 585 |
+
for target in targets:
|
| 586 |
+
if target == "hpe-slurm":
|
| 587 |
+
continue
|
| 588 |
+
agent_config = build_agent_bridge_config(
|
| 589 |
+
models,
|
| 590 |
+
target=target,
|
| 591 |
+
base_url=base_url,
|
| 592 |
+
host=args.host,
|
| 593 |
+
port=args.port,
|
| 594 |
+
cache_dir=args.cache_dir,
|
| 595 |
+
model_id=model.model_id,
|
| 596 |
+
)
|
| 597 |
+
target_dir = state_root / "agents" / target
|
| 598 |
+
agent_artifacts[target] = _write_agent_workspace_files(target_dir, agent_config)
|
| 599 |
+
if agent_artifacts:
|
| 600 |
+
artifacts["agents"] = agent_artifacts
|
| 601 |
+
|
| 602 |
+
if "hpe-slurm" in targets:
|
| 603 |
+
hpe_result = _write_hpe_init_bundle(
|
| 604 |
+
state_root / "hpe",
|
| 605 |
+
model,
|
| 606 |
+
port=args.port,
|
| 607 |
+
backend=args.backend,
|
| 608 |
+
backend_port=args.backend_port,
|
| 609 |
+
backend_base_url=args.backend_base_url,
|
| 610 |
+
backend_model=args.backend_model,
|
| 611 |
+
backend_command=args.backend_command,
|
| 612 |
+
api_key_env=args.api_key_env,
|
| 613 |
+
vllm_args=args.vllm_args,
|
| 614 |
+
gpus=args.gpus,
|
| 615 |
+
memory=args.memory,
|
| 616 |
+
time_limit=args.time,
|
| 617 |
+
partition=args.partition,
|
| 618 |
+
)
|
| 619 |
+
artifacts["hpe"] = hpe_result["written"]
|
| 620 |
+
|
| 621 |
+
if args.pull:
|
| 622 |
+
pulled = pull_model(models, model.model_id, cache_root=args.cache_dir)
|
| 623 |
+
artifacts["pulled_model"] = pulled.to_dict()
|
| 624 |
+
|
| 625 |
+
result = {
|
| 626 |
+
"ok": self_test.get("ok", True),
|
| 627 |
+
"model_id": model.model_id,
|
| 628 |
+
"repo_id": model.repo_id,
|
| 629 |
+
"runtime": model.runtime,
|
| 630 |
+
"targets": targets,
|
| 631 |
+
"artifacts": artifacts,
|
| 632 |
+
"config": config,
|
| 633 |
+
}
|
| 634 |
+
print(json.dumps(result, indent=2, sort_keys=True))
|
| 635 |
+
return 0 if result["ok"] or args.allow_warnings else 2
|
| 636 |
+
|
| 637 |
+
|
| 638 |
+
def _expand_agent_targets(raw_targets: Sequence[str], *, all_targets: bool, include_hpe: bool) -> list[str]:
|
| 639 |
+
selected: list[str] = []
|
| 640 |
+
raw_values: list[str] = []
|
| 641 |
+
for value in raw_targets:
|
| 642 |
+
raw_values.extend(part.strip() for part in value.split(",") if part.strip())
|
| 643 |
+
if all_targets or not raw_values:
|
| 644 |
+
raw_values.extend(_AGENT_SETUP_DEFAULT_TARGETS)
|
| 645 |
+
if include_hpe:
|
| 646 |
+
raw_values.append("hpe-slurm")
|
| 647 |
+
for value in raw_values:
|
| 648 |
+
if value.lower() == "all":
|
| 649 |
+
expanded = list(_AGENT_SETUP_DEFAULT_TARGETS)
|
| 650 |
+
else:
|
| 651 |
+
expanded = [value]
|
| 652 |
+
for item in expanded:
|
| 653 |
+
normalized = normalize_agent_target(item)
|
| 654 |
+
if normalized not in _AGENT_CANONICAL_TARGETS:
|
| 655 |
+
choices = ", ".join(_AGENT_TARGET_CHOICES)
|
| 656 |
+
raise ValueError(f"Unsupported agent target: {item}. Choices: {choices}")
|
| 657 |
+
if normalized not in selected:
|
| 658 |
+
selected.append(normalized)
|
| 659 |
+
return selected
|
| 660 |
+
|
| 661 |
+
|
| 662 |
+
def _write_agent_workspace_files(target_dir: Path, config: dict[str, Any]) -> list[str]:
|
| 663 |
+
target_dir.mkdir(parents=True, exist_ok=True)
|
| 664 |
+
config_path = target_dir / "marama-route.agent.json"
|
| 665 |
+
config_path.write_text(json.dumps(config, indent=2, sort_keys=True), encoding="utf-8")
|
| 666 |
+
written = [str(config_path)]
|
| 667 |
+
workspace_files = config.get("workspace_files", {})
|
| 668 |
+
if isinstance(workspace_files, dict):
|
| 669 |
+
for name, content in workspace_files.items():
|
| 670 |
+
output_path = target_dir / Path(str(name)).name
|
| 671 |
+
if isinstance(content, list):
|
| 672 |
+
text = "\n".join(str(line) for line in content).rstrip() + "\n"
|
| 673 |
+
else:
|
| 674 |
+
text = str(content).rstrip() + "\n"
|
| 675 |
+
output_path.write_text(text, encoding="utf-8")
|
| 676 |
+
written.append(str(output_path))
|
| 677 |
+
return written
|
| 678 |
+
|
| 679 |
+
|
| 680 |
+
def _write_hpe_init_bundle(
|
| 681 |
+
output_dir: Path,
|
| 682 |
+
model: Any,
|
| 683 |
+
*,
|
| 684 |
+
port: int,
|
| 685 |
+
backend: str,
|
| 686 |
+
backend_port: int = 8000,
|
| 687 |
+
backend_base_url: str = "",
|
| 688 |
+
backend_model: str = "",
|
| 689 |
+
backend_command: str = "",
|
| 690 |
+
api_key_env: str = "",
|
| 691 |
+
vllm_args: str = "",
|
| 692 |
+
gpus: int = 0,
|
| 693 |
+
memory: str = "32G",
|
| 694 |
+
time_limit: str = "02:00:00",
|
| 695 |
+
partition: str = "",
|
| 696 |
+
) -> dict[str, Any]:
|
| 697 |
+
output_dir.mkdir(parents=True, exist_ok=True)
|
| 698 |
+
backend_base = backend_base_url or f"http://127.0.0.1:{backend_port}/v1"
|
| 699 |
+
gateway_config = render_hpe_gateway_config(
|
| 700 |
+
model_id=model.model_id,
|
| 701 |
+
backend=backend,
|
| 702 |
+
model_runtime=model.runtime,
|
| 703 |
+
backend_base_url=backend_base,
|
| 704 |
+
backend_model=backend_model or model.model_id,
|
| 705 |
+
api_key_env=api_key_env,
|
| 706 |
+
cache_dir="$MARAMA_ROUTE_CACHE",
|
| 707 |
+
)
|
| 708 |
+
script = output_dir / "marama-route.slurm"
|
| 709 |
+
script.write_text(
|
| 710 |
+
render_hpe_slurm_script(
|
| 711 |
+
model_id=model.model_id,
|
| 712 |
+
repo_id=model.repo_id,
|
| 713 |
+
model_runtime=model.runtime,
|
| 714 |
+
port=port,
|
| 715 |
+
backend=backend,
|
| 716 |
+
backend_port=backend_port,
|
| 717 |
+
backend_base_url=backend_base_url,
|
| 718 |
+
backend_model=backend_model,
|
| 719 |
+
backend_command=backend_command,
|
| 720 |
+
api_key_env=api_key_env,
|
| 721 |
+
vllm_args=vllm_args,
|
| 722 |
+
gpus=gpus,
|
| 723 |
+
memory=memory,
|
| 724 |
+
time_limit=time_limit,
|
| 725 |
+
partition=partition,
|
| 726 |
+
),
|
| 727 |
+
encoding="utf-8",
|
| 728 |
+
)
|
| 729 |
+
gateway = output_dir / "gateway.hpe.json"
|
| 730 |
+
gateway.write_text(json.dumps(gateway_config, indent=2, sort_keys=True), encoding="utf-8")
|
| 731 |
+
env = output_dir / "marama-route.env"
|
| 732 |
+
env.write_text(
|
| 733 |
+
"\n".join(
|
| 734 |
+
(
|
| 735 |
+
"MARAMA_ROUTE_CACHE=${SCRATCH:-$HOME}/marama-route/models",
|
| 736 |
+
f"MARAMA_ROUTE_PORT={port}",
|
| 737 |
+
f"MARAMA_BACKEND_PORT={backend_port}",
|
| 738 |
+
f"MARAMA_BACKEND_BASE_URL={backend_base}",
|
| 739 |
+
f"MARAMA_BACKEND_MODEL={backend_model or model.model_id}",
|
| 740 |
+
f"MARAMA_BACKEND={backend}",
|
| 741 |
+
"",
|
| 742 |
+
),
|
| 743 |
+
),
|
| 744 |
+
encoding="utf-8",
|
| 745 |
+
)
|
| 746 |
+
definition = output_dir / "marama-route.def"
|
| 747 |
+
definition.write_text(render_hpe_apptainer_definition(backend=backend), encoding="utf-8")
|
| 748 |
+
readme = output_dir / "README.hpe.md"
|
| 749 |
+
readme.write_text(render_hpe_readme(model_id=model.model_id, port=port, backend=backend), encoding="utf-8")
|
| 750 |
+
return {
|
| 751 |
+
"ok": True,
|
| 752 |
+
"written": [str(script), str(gateway), str(env), str(definition), str(readme)],
|
| 753 |
+
"model_id": model.model_id,
|
| 754 |
+
"repo_id": model.repo_id,
|
| 755 |
+
"backend": backend,
|
| 756 |
+
"gateway_config": gateway_config,
|
| 757 |
+
}
|
| 758 |
+
|
| 759 |
+
|
| 760 |
def _writable_dir(path: Path) -> dict[str, Any]:
|
| 761 |
try:
|
| 762 |
path.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 916 |
model_ref = args.model or args.model_option
|
| 917 |
model = _resolve_cli_model(models, model_ref) if model_ref else _recommended_model(models)
|
| 918 |
target_dir = args.output_dir.resolve()
|
|
|
|
| 919 |
config = build_agent_bridge_config(
|
| 920 |
models,
|
| 921 |
target=args.target,
|
|
|
|
| 925 |
cache_dir=args.cache_dir,
|
| 926 |
model_id=model.model_id,
|
| 927 |
)
|
| 928 |
+
written = _write_agent_workspace_files(target_dir, config)
|
| 929 |
+
ok = bool(config.get("ok", True))
|
| 930 |
+
print(
|
| 931 |
+
json.dumps(
|
| 932 |
+
{"ok": ok, "target": config.get("target", args.target), "model_id": model.model_id, "written": written},
|
| 933 |
+
indent=2,
|
| 934 |
+
sort_keys=True,
|
| 935 |
+
),
|
| 936 |
+
)
|
| 937 |
+
return 0 if ok else 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 938 |
|
| 939 |
|
| 940 |
def _agent_doctor(args: argparse.Namespace) -> int:
|
|
|
|
| 953 |
health = _gateway_health(args.base_url)
|
| 954 |
checks = {
|
| 955 |
"registry": {"ok": bool(models), "count": len(models), "path": str(_registry_path(args))},
|
| 956 |
+
"target": {"ok": config.get("target") in _AGENT_CANONICAL_TARGETS, "value": config.get("target")},
|
| 957 |
"cache": {"ok": True, "path": str(args.cache_dir or default_cache_root())},
|
| 958 |
"gateway": health,
|
| 959 |
}
|
|
|
|
| 997 |
cache_dir="$MARAMA_ROUTE_CACHE",
|
| 998 |
)
|
| 999 |
if args.hpe_action == "init":
|
| 1000 |
+
result = _write_hpe_init_bundle(
|
| 1001 |
+
output_dir,
|
| 1002 |
+
model,
|
| 1003 |
+
port=args.port,
|
| 1004 |
+
backend=args.backend,
|
| 1005 |
+
backend_port=args.backend_port,
|
| 1006 |
+
backend_base_url=args.backend_base_url,
|
| 1007 |
+
backend_model=args.backend_model,
|
| 1008 |
+
backend_command=args.backend_command,
|
| 1009 |
+
api_key_env=args.api_key_env,
|
| 1010 |
+
vllm_args=args.vllm_args,
|
| 1011 |
+
gpus=args.gpus,
|
| 1012 |
+
memory=args.memory,
|
| 1013 |
+
time_limit=args.time,
|
| 1014 |
+
partition=args.partition,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1015 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1016 |
elif args.hpe_action == "submit":
|
| 1017 |
script = output_dir / "marama-route.slurm"
|
| 1018 |
script.write_text(
|
|
|
|
| 2110 |
"",
|
| 2111 |
"Start here: press Enter for hardware-suitable models, or type /help any time.",
|
| 2112 |
"Offline flow: /models -> choose -> /pull -> chat. Use /switch <text> to change.",
|
| 2113 |
+
"Production commands: setup | doctor | verify --deep | serve --live-local | agent-init | hpe init",
|
| 2114 |
"MaramaRoute serve --live-local --port 8787",
|
| 2115 |
],
|
| 2116 |
)
|
|
|
|
| 2414 |
print(f"- {name}: {mark}{detail}")
|
| 2415 |
if not result["checks"]["llama_cpp"]["ok"]:
|
| 2416 |
print("Install local GGUF runtime: python -m pip install llama-cpp-python")
|
| 2417 |
+
if not result["checks"]["tokenizer_extras"]["ok"]:
|
| 2418 |
+
print("Install tokenizer extras: python -m pip install sentencepiece tiktoken tokenizers")
|
| 2419 |
+
print("Next: MaramaRoute setup --all-targets --hpe | MaramaRoute chat | MaramaRoute serve --live-local --port 8787")
|
| 2420 |
|
| 2421 |
|
| 2422 |
def _ensure_model_ready(
|
|
|
|
| 2623 |
init.add_argument("--host", default="127.0.0.1")
|
| 2624 |
init.add_argument("--port", type=int, default=8787)
|
| 2625 |
init.add_argument("--live-local", action=argparse.BooleanOptionalAction, default=True)
|
| 2626 |
+
init.add_argument("--agent", default="", choices=("", *_AGENT_TARGET_CHOICES))
|
| 2627 |
init.add_argument("--hpe", action=argparse.BooleanOptionalAction, default=False)
|
| 2628 |
init.add_argument("--pull", action=argparse.BooleanOptionalAction, default=False)
|
| 2629 |
init.add_argument("--self-test", action=argparse.BooleanOptionalAction, default=True)
|
| 2630 |
init.set_defaults(handler=_init)
|
| 2631 |
|
| 2632 |
+
setup = subparsers.add_parser("setup", help="Create a production-ready MaramaRoute local, agent, and HPE setup.")
|
| 2633 |
+
setup.add_argument("model", nargs="?", help="Optional default model id, repo id, alias, or search fragment.")
|
| 2634 |
+
setup.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
|
| 2635 |
+
setup.add_argument("--cache-dir", type=Path, default=default_cache_root(), help="Local model cache directory.")
|
| 2636 |
+
setup.add_argument("--state-dir", type=Path, default=None, help="MaramaRoute state directory.")
|
| 2637 |
+
setup.add_argument("--host", default="127.0.0.1")
|
| 2638 |
+
setup.add_argument("--port", type=int, default=8787)
|
| 2639 |
+
setup.add_argument("--target", action="append", default=[], help="Agent target. Repeat or comma-separate values; use all for every coding target.")
|
| 2640 |
+
setup.add_argument("--all-targets", action=argparse.BooleanOptionalAction, default=False)
|
| 2641 |
+
setup.add_argument("--hpe", action=argparse.BooleanOptionalAction, default=False)
|
| 2642 |
+
setup.add_argument("--backend", choices=["auto", "local-live", "vllm", "nim", "nemo", "external"], default="auto")
|
| 2643 |
+
setup.add_argument("--backend-port", type=int, default=8000)
|
| 2644 |
+
setup.add_argument("--backend-base-url", default="")
|
| 2645 |
+
setup.add_argument("--backend-model", default="")
|
| 2646 |
+
setup.add_argument("--backend-command", default="")
|
| 2647 |
+
setup.add_argument("--api-key-env", default="")
|
| 2648 |
+
setup.add_argument("--vllm-args", default="")
|
| 2649 |
+
setup.add_argument("--gpus", type=int, default=0)
|
| 2650 |
+
setup.add_argument("--memory", default="32G")
|
| 2651 |
+
setup.add_argument("--time", default="02:00:00")
|
| 2652 |
+
setup.add_argument("--partition", default="")
|
| 2653 |
+
setup.add_argument("--pull", action=argparse.BooleanOptionalAction, default=False)
|
| 2654 |
+
setup.add_argument("--allow-warnings", action=argparse.BooleanOptionalAction, default=True)
|
| 2655 |
+
setup.set_defaults(handler=_setup)
|
| 2656 |
+
|
| 2657 |
hardware = subparsers.add_parser("hardware", help="Inspect local hardware and recommend runnable LumynaX models.")
|
| 2658 |
hardware.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
|
| 2659 |
hardware.add_argument("--cache-dir", type=Path, default=default_cache_root(), help="Local model cache directory.")
|
|
|
|
| 2750 |
help="Emit command-bridge config for coding agents, local gateways, or HPE/Slurm jobs.",
|
| 2751 |
)
|
| 2752 |
agent.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
|
| 2753 |
+
agent.add_argument("--target", default="generic", choices=_AGENT_TARGET_CHOICES)
|
| 2754 |
agent.add_argument("--model", default="", help="Default model id, repo id, or unique search fragment.")
|
| 2755 |
agent.add_argument("--base-url", default="http://127.0.0.1:8787/v1")
|
| 2756 |
agent.add_argument("--host", default="127.0.0.1")
|
|
|
|
| 2763 |
agent_init.add_argument("model", nargs="?", help="Optional default model id, repo id, alias, or search fragment.")
|
| 2764 |
agent_init.add_argument("--model", dest="model_option", default="", help="Default model id, repo id, alias, or search fragment.")
|
| 2765 |
agent_init.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
|
| 2766 |
+
agent_init.add_argument("--target", default="claude-code", choices=_AGENT_TARGET_CHOICES)
|
| 2767 |
agent_init.add_argument("--output-dir", type=Path, default=Path("."))
|
| 2768 |
agent_init.add_argument("--base-url", default="http://127.0.0.1:8787/v1")
|
| 2769 |
agent_init.add_argument("--host", default="127.0.0.1")
|
|
|
|
| 2773 |
|
| 2774 |
agent_doctor = subparsers.add_parser("agent-doctor", help="Check agent bridge config and local gateway readiness.")
|
| 2775 |
agent_doctor.add_argument("--registry", type=Path, default=None, help="MaramaRoute model registry JSON.")
|
| 2776 |
+
agent_doctor.add_argument("--target", default="claude-code", choices=_AGENT_TARGET_CHOICES)
|
| 2777 |
agent_doctor.add_argument("--model", default="", help="Default model id, repo id, alias, or search fragment.")
|
| 2778 |
agent_doctor.add_argument("--base-url", default="http://127.0.0.1:8787/v1")
|
| 2779 |
agent_doctor.add_argument("--host", default="127.0.0.1")
|
marama_route/platform.py
CHANGED
|
@@ -283,6 +283,23 @@ def recommend_model(
|
|
| 283 |
}
|
| 284 |
|
| 285 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
def build_agent_bridge_config(
|
| 287 |
models: tuple[ModelEndpoint, ...],
|
| 288 |
*,
|
|
@@ -293,7 +310,7 @@ def build_agent_bridge_config(
|
|
| 293 |
cache_dir: Path | None = None,
|
| 294 |
model_id: str = "",
|
| 295 |
) -> dict[str, Any]:
|
| 296 |
-
normalized =
|
| 297 |
selected = _select_export_model(models, model_id)
|
| 298 |
cache_value = str(cache_dir) if cache_dir is not None else "${MARAMA_ROUTE_CACHE:-~/.cache/abteex-ai/marama-route/models}"
|
| 299 |
commands = {
|
|
@@ -303,12 +320,22 @@ def build_agent_bridge_config(
|
|
| 303 |
"recommend": "MaramaRoute recommend --task code --sensitivity restricted --prompt-text \"Describe the task\"",
|
| 304 |
"pull": f"MaramaRoute pull {selected.model_id}" if selected is not None else "MaramaRoute pull <model-id>",
|
| 305 |
"chat": f"MaramaRoute chat {selected.model_id}" if selected is not None else "MaramaRoute chat <model-id>",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
}
|
|
|
|
|
|
|
| 307 |
config: dict[str, Any] = {
|
| 308 |
"ok": True,
|
| 309 |
"target": normalized,
|
| 310 |
"product": "LumynaX MaramaRoute",
|
| 311 |
"mode": "local_command_bridge",
|
|
|
|
| 312 |
"base_url": base_url,
|
| 313 |
"cache_dir": cache_value,
|
| 314 |
"default_model": model_summary(selected) if selected is not None else None,
|
|
@@ -319,7 +346,7 @@ def build_agent_bridge_config(
|
|
| 319 |
"ABTEEX_MARAMA_API_KEY": "local-dev",
|
| 320 |
},
|
| 321 |
}
|
| 322 |
-
if normalized
|
| 323 |
config["target"] = "claude-code"
|
| 324 |
config["workspace_files"] = {
|
| 325 |
"CLAUDE.md": [
|
|
@@ -332,7 +359,82 @@ def build_agent_bridge_config(
|
|
| 332 |
"This export is a command bridge. It does not replace the coding agent runtime.",
|
| 333 |
"Keep sensitive prompts on the local MaramaRoute path when project policy requires it.",
|
| 334 |
]
|
| 335 |
-
elif normalized
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
config["target"] = "hpe-slurm"
|
| 337 |
config["environment"].update(
|
| 338 |
{
|
|
@@ -359,6 +461,18 @@ def build_agent_bridge_config(
|
|
| 359 |
}
|
| 360 |
elif normalized == "opencode":
|
| 361 |
config["provider_config"] = build_opencode_provider_config(models, base_url=base_url)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
return config
|
| 363 |
|
| 364 |
|
|
|
|
| 283 |
}
|
| 284 |
|
| 285 |
|
| 286 |
+
def normalize_agent_target(target: str) -> str:
|
| 287 |
+
normalized = target.strip().lower().replace("_", "-")
|
| 288 |
+
aliases = {
|
| 289 |
+
"claude": "claude-code",
|
| 290 |
+
"claude-code-cli": "claude-code",
|
| 291 |
+
"codex-cli": "codex",
|
| 292 |
+
"continue-dev": "continue",
|
| 293 |
+
"lite-llm": "litellm",
|
| 294 |
+
"lite-llm-proxy": "litellm",
|
| 295 |
+
"tabbyml": "tabby",
|
| 296 |
+
"hpe": "hpe-slurm",
|
| 297 |
+
"hpc": "hpe-slurm",
|
| 298 |
+
"slurm": "hpe-slurm",
|
| 299 |
+
}
|
| 300 |
+
return aliases.get(normalized, normalized or "generic")
|
| 301 |
+
|
| 302 |
+
|
| 303 |
def build_agent_bridge_config(
|
| 304 |
models: tuple[ModelEndpoint, ...],
|
| 305 |
*,
|
|
|
|
| 310 |
cache_dir: Path | None = None,
|
| 311 |
model_id: str = "",
|
| 312 |
) -> dict[str, Any]:
|
| 313 |
+
normalized = normalize_agent_target(target)
|
| 314 |
selected = _select_export_model(models, model_id)
|
| 315 |
cache_value = str(cache_dir) if cache_dir is not None else "${MARAMA_ROUTE_CACHE:-~/.cache/abteex-ai/marama-route/models}"
|
| 316 |
commands = {
|
|
|
|
| 320 |
"recommend": "MaramaRoute recommend --task code --sensitivity restricted --prompt-text \"Describe the task\"",
|
| 321 |
"pull": f"MaramaRoute pull {selected.model_id}" if selected is not None else "MaramaRoute pull <model-id>",
|
| 322 |
"chat": f"MaramaRoute chat {selected.model_id}" if selected is not None else "MaramaRoute chat <model-id>",
|
| 323 |
+
"doctor": "MaramaRoute doctor --hardware",
|
| 324 |
+
"agent_doctor": f"MaramaRoute agent doctor --target {normalized}",
|
| 325 |
+
"setup": "MaramaRoute setup --all-targets --hpe",
|
| 326 |
+
"hpe_init": f"MaramaRoute hpe init {selected.model_id if selected else '<model-id>'} --backend auto",
|
| 327 |
+
"compat_vllm": "MaramaRoute compat --target vllm --status usable",
|
| 328 |
+
"compat_nim": "MaramaRoute compat --target nim --status usable",
|
| 329 |
+
"compat_nemo": "MaramaRoute compat --target nemo --status pathway",
|
| 330 |
}
|
| 331 |
+
selected_id = selected.model_id if selected is not None else "<model-id>"
|
| 332 |
+
agent_targets = ("generic", "claude-code", "codex", "continue", "opencode", "litellm", "tabby", "hpe-slurm")
|
| 333 |
config: dict[str, Any] = {
|
| 334 |
"ok": True,
|
| 335 |
"target": normalized,
|
| 336 |
"product": "LumynaX MaramaRoute",
|
| 337 |
"mode": "local_command_bridge",
|
| 338 |
+
"supported_targets": list(agent_targets),
|
| 339 |
"base_url": base_url,
|
| 340 |
"cache_dir": cache_value,
|
| 341 |
"default_model": model_summary(selected) if selected is not None else None,
|
|
|
|
| 346 |
"ABTEEX_MARAMA_API_KEY": "local-dev",
|
| 347 |
},
|
| 348 |
}
|
| 349 |
+
if normalized == "claude-code":
|
| 350 |
config["target"] = "claude-code"
|
| 351 |
config["workspace_files"] = {
|
| 352 |
"CLAUDE.md": [
|
|
|
|
| 359 |
"This export is a command bridge. It does not replace the coding agent runtime.",
|
| 360 |
"Keep sensitive prompts on the local MaramaRoute path when project policy requires it.",
|
| 361 |
]
|
| 362 |
+
elif normalized == "codex":
|
| 363 |
+
config["workspace_files"] = {
|
| 364 |
+
"AGENTS.md": [
|
| 365 |
+
"# MaramaRoute",
|
| 366 |
+
"",
|
| 367 |
+
"Use MaramaRoute for LumynaX model selection, local chat, and local-live gateway work.",
|
| 368 |
+
f"Default model: `{selected_id}`",
|
| 369 |
+
f"Start gateway: `{commands['start_gateway']}`",
|
| 370 |
+
f"Pull model files: `{commands['pull']}`",
|
| 371 |
+
f"Run local chat: `{commands['chat']}`",
|
| 372 |
+
],
|
| 373 |
+
}
|
| 374 |
+
config["notes"] = [
|
| 375 |
+
"This export gives coding tools project-local commands and policy hints.",
|
| 376 |
+
"Start the MaramaRoute gateway only when a project needs a local route API.",
|
| 377 |
+
]
|
| 378 |
+
elif normalized == "continue":
|
| 379 |
+
config["continue_config"] = {
|
| 380 |
+
"name": "LumynaX MaramaRoute",
|
| 381 |
+
"model": selected_id,
|
| 382 |
+
"base_url": base_url,
|
| 383 |
+
"api_key_env": "ABTEEX_MARAMA_API_KEY",
|
| 384 |
+
"start_command": commands["start_gateway"],
|
| 385 |
+
}
|
| 386 |
+
config["workspace_files"] = {
|
| 387 |
+
"continue.marama-route.json": [
|
| 388 |
+
json.dumps(config["continue_config"], indent=2, sort_keys=True),
|
| 389 |
+
],
|
| 390 |
+
}
|
| 391 |
+
config["notes"] = [
|
| 392 |
+
"Use this as the project-local Continue bridge reference.",
|
| 393 |
+
"Run the gateway command before pointing Continue at the local MaramaRoute endpoint.",
|
| 394 |
+
]
|
| 395 |
+
elif normalized == "litellm":
|
| 396 |
+
config["litellm_config"] = {
|
| 397 |
+
"model_list": [
|
| 398 |
+
{
|
| 399 |
+
"model_name": selected_id,
|
| 400 |
+
"litellm_params": {
|
| 401 |
+
"model": f"marama-route/{selected_id}",
|
| 402 |
+
"api_base": base_url,
|
| 403 |
+
"api_key": "os.environ/ABTEEX_MARAMA_API_KEY",
|
| 404 |
+
},
|
| 405 |
+
},
|
| 406 |
+
],
|
| 407 |
+
"router_settings": {
|
| 408 |
+
"routing_strategy": "simple-shuffle",
|
| 409 |
+
"num_retries": 1,
|
| 410 |
+
},
|
| 411 |
+
}
|
| 412 |
+
config["workspace_files"] = {
|
| 413 |
+
"litellm.marama-route.json": [
|
| 414 |
+
json.dumps(config["litellm_config"], indent=2, sort_keys=True),
|
| 415 |
+
],
|
| 416 |
+
}
|
| 417 |
+
config["notes"] = [
|
| 418 |
+
"This file is a local gateway reference for LiteLLM-style proxy deployments.",
|
| 419 |
+
"Keep the MaramaRoute gateway running while the proxy sends model traffic.",
|
| 420 |
+
]
|
| 421 |
+
elif normalized == "tabby":
|
| 422 |
+
config["tabby_config"] = {
|
| 423 |
+
"model": selected_id,
|
| 424 |
+
"base_url": base_url,
|
| 425 |
+
"start_command": commands["start_gateway"],
|
| 426 |
+
"pull_command": commands["pull"],
|
| 427 |
+
}
|
| 428 |
+
config["workspace_files"] = {
|
| 429 |
+
"tabby.marama-route.json": [
|
| 430 |
+
json.dumps(config["tabby_config"], indent=2, sort_keys=True),
|
| 431 |
+
],
|
| 432 |
+
}
|
| 433 |
+
config["notes"] = [
|
| 434 |
+
"Use this as a Tabby workspace note for routing local coding tasks through LumynaX models.",
|
| 435 |
+
"Verify model quality with `MaramaRoute bench` before production use.",
|
| 436 |
+
]
|
| 437 |
+
elif normalized == "hpe-slurm":
|
| 438 |
config["target"] = "hpe-slurm"
|
| 439 |
config["environment"].update(
|
| 440 |
{
|
|
|
|
| 461 |
}
|
| 462 |
elif normalized == "opencode":
|
| 463 |
config["provider_config"] = build_opencode_provider_config(models, base_url=base_url)
|
| 464 |
+
config["workspace_files"] = {
|
| 465 |
+
"opencode.marama-route.json": [
|
| 466 |
+
json.dumps(config["provider_config"], indent=2, sort_keys=True),
|
| 467 |
+
],
|
| 468 |
+
}
|
| 469 |
+
config["notes"] = [
|
| 470 |
+
"Drop the provider config into the workspace or user config directory used by OpenCode.",
|
| 471 |
+
"Run the MaramaRoute gateway first when using live local routing.",
|
| 472 |
+
]
|
| 473 |
+
elif normalized not in agent_targets:
|
| 474 |
+
config["ok"] = False
|
| 475 |
+
config["error"] = f"Unsupported agent target: {target}"
|
| 476 |
return config
|
| 477 |
|
| 478 |
|
pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "lumynax-marama-route"
|
| 7 |
-
version = "0.
|
| 8 |
description = "LumynaX MaramaRoute: conversational AbteeX AI Labs model CLI, downloader, and local runtime for LumynaX releases."
|
| 9 |
readme = "README.md"
|
| 10 |
requires-python = ">=3.11"
|
|
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "lumynax-marama-route"
|
| 7 |
+
version = "0.8.0"
|
| 8 |
description = "LumynaX MaramaRoute: conversational AbteeX AI Labs model CLI, downloader, and local runtime for LumynaX releases."
|
| 9 |
readme = "README.md"
|
| 10 |
requires-python = ">=3.11"
|