Enhance CLI and documentation for capturing VM inventory

- Introduced new `capture` commands in the CLI to allow users to merge VM inventory into local manifests without downloading weights.
- Updated `README.md` and `cli.md` to include detailed instructions for the new capture functionality, including options for models and extensions.
- Enhanced `decisions.md` to clarify the role of captured links in the manifest files.
- Improved `extensions.md` to document the process of capturing installed extensions back to the local configuration.
- Added new functions in `civitai.py` to support fetching model versions by hash and generating canonical URLs for models.
This commit is contained in:
Leonid Pershin
2026-08-21 05:56:52 +03:00
parent 71f4e4c2e3
commit 603165a4ba
10 changed files with 940 additions and 3 deletions
+114
View File
@@ -683,6 +683,120 @@ def seed_extensions_cmd() -> None:
_die(exc)
capture_app = typer.Typer(
help=(
"Снять с VM инвентарь → локальные манифесты (только ссылки, без весов). "
"Merge в models.yaml / extensions.yaml."
),
no_args_is_help=False,
)
app.add_typer(capture_app, name="capture")
@capture_app.callback(invoke_without_command=True)
def capture_root(
ctx: typer.Context,
dry_run: bool = typer.Option(False, "--dry-run", help="Не писать yaml, только отчёт"),
kind: Optional[str] = typer.Option(
None, "--kind", help="Только models: checkpoint|lora|vae|…"
),
) -> None:
"""Без подкоманды — capture all."""
if ctx.invoked_subcommand is not None:
return
try:
from gpu_rent.capture import capture_all, print_report
from gpu_rent.manifests import MODEL_TYPES
if kind and kind not in MODEL_TYPES:
raise GpuRentError(f"--kind: жду один из {', '.join(MODEL_TYPES)}")
cfg, host = _live()
report = capture_all(
cfg,
host,
dry_run=dry_run,
kind_filter=kind,
log=lambda m: console.print(m),
)
print_report(report, lambda m: console.print(m), dry_run=dry_run)
except GpuRentError as exc:
_die(exc)
@capture_app.command("models")
def capture_models_cmd(
dry_run: bool = typer.Option(False, "--dry-run"),
kind: Optional[str] = typer.Option(
None, "--kind", help="checkpoint|lora|vae|embedding|controlnet|upscaler|clip"
),
) -> None:
"""Models на VM → merge Civitai url в models.yaml."""
try:
from gpu_rent.capture import capture_models, print_report
from gpu_rent.manifests import MODEL_TYPES
if kind and kind not in MODEL_TYPES:
raise GpuRentError(f"--kind: жду один из {', '.join(MODEL_TYPES)}")
cfg, host = _live()
report = capture_models(
cfg,
host,
None,
dry_run=dry_run,
kind_filter=kind,
log=lambda m: console.print(m),
)
print_report(report, lambda m: console.print(m), dry_run=dry_run)
except GpuRentError as exc:
_die(exc)
@capture_app.command("extensions")
def capture_extensions_cmd(
dry_run: bool = typer.Option(False, "--dry-run"),
) -> None:
"""Extensions/DLNodes на VM → merge git url в extensions.yaml."""
try:
from gpu_rent.capture import capture_extensions, print_report
cfg, host = _live()
report = capture_extensions(
cfg,
host,
None,
dry_run=dry_run,
log=lambda m: console.print(m),
)
print_report(report, lambda m: console.print(m), dry_run=dry_run)
except GpuRentError as exc:
_die(exc)
@capture_app.command("all")
def capture_all_cmd(
dry_run: bool = typer.Option(False, "--dry-run"),
kind: Optional[str] = typer.Option(None, "--kind", help="Фильтр только для models"),
) -> None:
"""models + extensions."""
try:
from gpu_rent.capture import capture_all, print_report
from gpu_rent.manifests import MODEL_TYPES
if kind and kind not in MODEL_TYPES:
raise GpuRentError(f"--kind: жду один из {', '.join(MODEL_TYPES)}")
cfg, host = _live()
report = capture_all(
cfg,
host,
dry_run=dry_run,
kind_filter=kind,
log=lambda m: console.print(m),
)
print_report(report, lambda m: console.print(m), dry_run=dry_run)
except GpuRentError as exc:
_die(exc)
@app.command("resize-data")
def resize_data(gb: int = typer.Option(..., "--gb", help="Новый размер data volume, GB (только вверх)")) -> None:
"""Cinder extend data volume + resize2fs на VM."""