Skip to content
Draft
3 changes: 2 additions & 1 deletion StabilityMatrix.Core/Helper/Factory/PackageFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ public BasePackage GetNewBasePackage(InstalledPackage installedPackage)
downloadService,
prerequisiteHelper,
pyInstallationManager,
pipWheelService
pipWheelService,
rocmPackageHelper
),
"RuinedFooocus" => new RuinedFooocus(
githubApiCache,
Expand Down
99 changes: 91 additions & 8 deletions StabilityMatrix.Core/Models/Packages/OneTrainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
using StabilityMatrix.Core.Helper.HardwareInfo;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Models.Rocm;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
using StabilityMatrix.Core.Services.Rocm;

namespace StabilityMatrix.Core.Models.Packages;

Expand All @@ -18,7 +20,8 @@ public class OneTrainer(
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper,
IPyInstallationManager pyInstallationManager,
IPipWheelService pipWheelService
IPipWheelService pipWheelService,
IRocmPackageHelper rocmPackageHelper
)
: BaseGitPackage(
githubApi,
Expand Down Expand Up @@ -52,6 +55,17 @@ IPipWheelService pipWheelService
public override bool ShouldIgnoreReleases => true;
public override IEnumerable<PackagePrerequisite> Prerequisites =>
base.Prerequisites.Concat([PackagePrerequisite.Tkinter]);
public override PyVersion RecommendedPythonVersion => Python.PyInstallationManager.Python_3_12_10;

public override TorchIndex GetRecommendedTorchVersion()
{
if (Compat.IsWindows && rocmPackageHelper.GetCompatibility().IsCompatible)
{
return TorchIndex.Rocm;
}

return base.GetRecommendedTorchVersion();
}

public override async Task InstallPackage(
string installLocation,
Expand All @@ -69,17 +83,66 @@ public override async Task InstallPackage(
)
.ConfigureAwait(false);

progress?.Report(new ProgressReport(-1f, "Installing requirements", isIndeterminate: true));
var torchVersion = options.PythonOptions.TorchIndex ?? GetRecommendedTorchVersion();
var requirementsFileName = torchVersion switch
var pyVersion = options.PythonOptions.PythonVersion ?? RecommendedPythonVersion;

// Windows ROCm path
var isWindowsRocm =
Compat.IsWindows
&& torchVersion == TorchIndex.Rocm
&& rocmPackageHelper.GetCompatibility().IsCompatible;

if (isWindowsRocm)
{
var config = rocmPackageHelper.BuildWindowsNativeInstallConfig(
OneTrainerWindowsRocmProfile.Default
);

await StandardPipInstallProcessAsync(
venvRunner,
options,
installedPackage,
config with
{
RequirementsFilePaths = ["requirements-default.txt"],
},
onConsoleOutput,
progress,
cancellationToken
)
.ConfigureAwait(false);

progress?.Report(
new ProgressReport(-1f, "Installing Windows ROCm torch...", isIndeterminate: true)
);

await rocmPackageHelper
.InstallWindowsNativeTorchAsync(
venvRunner,
installedPackage,
OneTrainerWindowsRocmProfile.CreateInstallProfile(pyVersion),
progress,
onConsoleOutput,
cancellationToken
)
.ConfigureAwait(false);
}
else
{
TorchIndex.Cuda => "requirements-cuda.txt",
TorchIndex.Rocm => "requirements-rocm.txt",
_ => "requirements-default.txt",
};
// Original path (CUDA / Linux ROCm)
progress?.Report(new ProgressReport(-1f, "Installing requirements", isIndeterminate: true));

await venvRunner.PipInstall(["-r", requirementsFileName], onConsoleOutput).ConfigureAwait(false);
var requirementsFileName = torchVersion switch
{
TorchIndex.Cuda => "requirements-cuda.txt",
TorchIndex.Rocm => "requirements-rocm.txt",
_ => "requirements-default.txt",
};

await venvRunner.PipInstall(["-r", requirementsFileName], onConsoleOutput).ConfigureAwait(false);
}

// Shared requirements-global.txt (both paths)
var requirementsGlobal = new FilePath(installLocation, "requirements-global.txt");
var pipArgs = new PipInstallArgs().WithParsedFromRequirementsTxt(
(await requirementsGlobal.ReadAllTextAsync(cancellationToken).ConfigureAwait(false)).Replace(
Expand Down Expand Up @@ -108,13 +171,33 @@ public override async Task RunPackage(
await SetupVenv(installLocation, pythonVersion: PyVersion.Parse(installedPackage.PythonVersion))
.ConfigureAwait(false);

var selectedTorchIndex = installedPackage.PreferredTorchIndex ?? GetRecommendedTorchVersion();

if (rocmPackageHelper.ShouldApplyWindowsLaunchEnvironment(selectedTorchIndex))
{
VenvRunner.UpdateEnvironmentVariables(env =>
env.SetItems(rocmPackageHelper.BuildLaunchEnvironment(OneTrainerWindowsRocmProfile.Default))
);
}

foreach (var line in GetLaunchNoticeLines(installedPackage))
{
onConsoleOutput?.Invoke(ProcessOutput.FromStdOutLine($"{line}{Environment.NewLine}"));
}

VenvRunner.RunDetached(
[Path.Combine(installLocation, options.Command ?? LaunchCommand), .. options.Arguments],
onConsoleOutput,
OnExit
);
}

private IReadOnlyList<string> GetLaunchNoticeLines(InstalledPackage installedPackage)
{
var selectedTorchIndex = installedPackage.PreferredTorchIndex ?? GetRecommendedTorchVersion();
return rocmPackageHelper.GetWindowsLaunchNoticeLines(selectedTorchIndex);
}

public override List<LaunchOptionDefinition> LaunchOptions => [LaunchOptionDefinition.Extras];
public override Dictionary<SharedFolderType, IReadOnlyList<string>>? SharedFolders { get; }
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders { get; }
Expand Down
39 changes: 39 additions & 0 deletions StabilityMatrix.Core/Models/Rocm/OneTrainerWindowsRocmProfile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Python;

namespace StabilityMatrix.Core.Models.Rocm;

/// <summary>
/// Shared Windows ROCm profile for OneTrainer.
/// Python 3.12 only - required by compatible bitsandbytes wheel.
/// </summary>
public class OneTrainerWindowsRocmProfile : RocmPackageProfile
{
public static RocmPackageProfile Default { get; } = new OneTrainerWindowsRocmProfile();

// Restores flop counter functionality requiring triton module
private const string TritonWindowsPackage = "triton-windows";

// Replace upstream bitsandbytes with ROCm-aware bitsandbytes for ROCm Technical Preview on Windows
private const string BitsAndBytesWheelUrl =
"https://github.com/0xDELUXA/bitsandbytes_win_rocm/releases/download/0.50.0.dev0-py3.12-rocm7.15-win_amd64_all/bitsandbytes-0.50.0.dev0-cp312-cp312-win_amd64.whl";

public static RocmPackageProfile CreateInstallProfile(PyVersion pyVersion)
{
if (pyVersion.Major == 3 && pyVersion.Minor == 12)
{
return new RocmPackageProfile
{
InstallConfig = new PipInstallConfig
{
PostTorchInstallPipArgs = [TritonWindowsPackage, BitsAndBytesWheelUrl],
},
};
}

return new RocmPackageProfile
{
InstallConfig = new PipInstallConfig { PostTorchInstallPipArgs = [TritonWindowsPackage] },
};
}
}
Loading