Files
git.stella-ops.org/src/Cli/StellaOps.Cli/Commands/ModelCommandGroup.cs

304 lines
10 KiB
C#

using System.CommandLine;
using StellaOps.Cli.Extensions;
namespace StellaOps.Cli.Commands;
/// <summary>
/// CLI commands for AI model bundle management.
/// Sprint: SPRINT_20251226_019_AI_offline_inference
/// Task: OFFLINE-13, OFFLINE-14
/// </summary>
internal static class ModelCommandGroup
{
internal static Command BuildModelCommand(
IServiceProvider services,
Option<bool> verboseOption,
CancellationToken cancellationToken)
{
var model = new Command("model", "AI model bundle management for offline inference.");
model.Add(BuildModelListCommand(services, verboseOption, cancellationToken));
model.Add(BuildModelPullCommand(services, verboseOption, cancellationToken));
model.Add(BuildModelVerifyCommand(services, verboseOption, cancellationToken));
model.Add(BuildModelInfoCommand(services, verboseOption, cancellationToken));
model.Add(BuildModelRemoveCommand(services, verboseOption, cancellationToken));
return model;
}
private static Command BuildModelListCommand(
IServiceProvider services,
Option<bool> verboseOption,
CancellationToken cancellationToken)
{
var bundlePathOption = new Option<string?>("--bundle-path", new[] { "-p" })
{
Description = "Path to model bundles directory (defaults to ~/.stellaops/models)."
};
var outputOption = new Option<string?>("--output", new[] { "-o" })
{
Description = "Output format: table (default), json."
}.SetDefaultValue("table").FromAmong("table", "json");
var command = new Command("list", "List available model bundles.")
{
bundlePathOption,
outputOption,
verboseOption
};
command.SetAction(async parseResult =>
{
var bundlePath = parseResult.GetValue(bundlePathOption);
var output = parseResult.GetValue(outputOption) ?? "table";
var verbose = parseResult.GetValue(verboseOption);
await CommandHandlers.HandleModelListAsync(
services,
bundlePath,
output,
verbose,
cancellationToken);
});
return command;
}
private static Command BuildModelPullCommand(
IServiceProvider services,
Option<bool> verboseOption,
CancellationToken cancellationToken)
{
var modelNameArg = new Argument<string>("model-name")
{
Description = "Model name to pull (e.g., llama3-8b, mistral-7b, phi-3)."
};
var quantOption = new Option<string?>("--quant", new[] { "-q" })
{
Description = "Quantization level (e.g., Q4_K_M, Q5_K_M, FP16)."
}.SetDefaultValue("Q4_K_M");
var offlineOption = new Option<bool>("--offline")
{
Description = "Pull from local cache or USB transfer (no network)."
};
var sourceOption = new Option<string?>("--source", new[] { "-s" })
{
Description = "Source path for offline pull (USB mount, network share)."
};
var bundlePathOption = new Option<string?>("--bundle-path", new[] { "-p" })
{
Description = "Destination path for model bundles (defaults to ~/.stellaops/models)."
};
var verifyOption = new Option<bool>("--verify")
{
Description = "Verify bundle integrity after pull."
}.SetDefaultValue(true);
var outputOption = new Option<string?>("--output", new[] { "-o" })
{
Description = "Output format: table (default), json."
}.SetDefaultValue("table").FromAmong("table", "json");
var command = new Command("pull", "Pull a model bundle for offline inference.")
{
modelNameArg,
quantOption,
offlineOption,
sourceOption,
bundlePathOption,
verifyOption,
outputOption,
verboseOption
};
command.SetAction(async parseResult =>
{
var modelName = parseResult.GetValue(modelNameArg) ?? string.Empty;
var quant = parseResult.GetValue(quantOption) ?? "Q4_K_M";
var offline = parseResult.GetValue(offlineOption);
var source = parseResult.GetValue(sourceOption);
var bundlePath = parseResult.GetValue(bundlePathOption);
var verify = parseResult.GetValue(verifyOption);
var output = parseResult.GetValue(outputOption) ?? "table";
var verbose = parseResult.GetValue(verboseOption);
await CommandHandlers.HandleModelPullAsync(
services,
modelName,
quant,
offline,
source,
bundlePath,
verify,
output,
verbose,
cancellationToken);
});
return command;
}
private static Command BuildModelVerifyCommand(
IServiceProvider services,
Option<bool> verboseOption,
CancellationToken cancellationToken)
{
var modelNameArg = new Argument<string>("model-name")
{
Description = "Model name or path to verify."
};
var bundlePathOption = new Option<string?>("--bundle-path", new[] { "-p" })
{
Description = "Path to model bundles directory (defaults to ~/.stellaops/models)."
};
var checkSignatureOption = new Option<bool>("--check-signature")
{
Description = "Verify cryptographic signature on the bundle."
}.SetDefaultValue(true);
var trustRootOption = new Option<string?>("--trust-root")
{
Description = "Path to trust root public key for signature verification."
};
var outputOption = new Option<string?>("--output", new[] { "-o" })
{
Description = "Output format: table (default), json."
}.SetDefaultValue("table").FromAmong("table", "json");
var command = new Command("verify", "Verify model bundle integrity and signature.")
{
modelNameArg,
bundlePathOption,
checkSignatureOption,
trustRootOption,
outputOption,
verboseOption
};
command.SetAction(async parseResult =>
{
var modelName = parseResult.GetValue(modelNameArg) ?? string.Empty;
var bundlePath = parseResult.GetValue(bundlePathOption);
var checkSignature = parseResult.GetValue(checkSignatureOption);
var trustRoot = parseResult.GetValue(trustRootOption);
var output = parseResult.GetValue(outputOption) ?? "table";
var verbose = parseResult.GetValue(verboseOption);
await CommandHandlers.HandleModelVerifyAsync(
services,
modelName,
bundlePath,
checkSignature,
trustRoot,
output,
verbose,
cancellationToken);
});
return command;
}
private static Command BuildModelInfoCommand(
IServiceProvider services,
Option<bool> verboseOption,
CancellationToken cancellationToken)
{
var modelNameArg = new Argument<string>("model-name")
{
Description = "Model name to get information for."
};
var bundlePathOption = new Option<string?>("--bundle-path", new[] { "-p" })
{
Description = "Path to model bundles directory (defaults to ~/.stellaops/models)."
};
var outputOption = new Option<string?>("--output", new[] { "-o" })
{
Description = "Output format: table (default), json."
}.SetDefaultValue("table").FromAmong("table", "json");
var command = new Command("info", "Display model bundle metadata and requirements.")
{
modelNameArg,
bundlePathOption,
outputOption,
verboseOption
};
command.SetAction(async parseResult =>
{
var modelName = parseResult.GetValue(modelNameArg) ?? string.Empty;
var bundlePath = parseResult.GetValue(bundlePathOption);
var output = parseResult.GetValue(outputOption) ?? "table";
var verbose = parseResult.GetValue(verboseOption);
await CommandHandlers.HandleModelInfoAsync(
services,
modelName,
bundlePath,
output,
verbose,
cancellationToken);
});
return command;
}
private static Command BuildModelRemoveCommand(
IServiceProvider services,
Option<bool> verboseOption,
CancellationToken cancellationToken)
{
var modelNameArg = new Argument<string>("model-name")
{
Description = "Model name to remove."
};
var bundlePathOption = new Option<string?>("--bundle-path", new[] { "-p" })
{
Description = "Path to model bundles directory (defaults to ~/.stellaops/models)."
};
var forceOption = new Option<bool>("--force", new[] { "-f" })
{
Description = "Force removal without confirmation."
};
var command = new Command("remove", "Remove a model bundle.")
{
modelNameArg,
bundlePathOption,
forceOption,
verboseOption
};
command.SetAction(async parseResult =>
{
var modelName = parseResult.GetValue(modelNameArg) ?? string.Empty;
var bundlePath = parseResult.GetValue(bundlePathOption);
var force = parseResult.GetValue(forceOption);
var verbose = parseResult.GetValue(verboseOption);
await CommandHandlers.HandleModelRemoveAsync(
services,
modelName,
bundlePath,
force,
verbose,
cancellationToken);
});
return command;
}
}