Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/LinML.Cli/InferencePipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public static SelectionOutcome Select(
if (!report.AvailableProviders.Contains(candidate)
&& KnownPlugins.All.Any(p => p.Id == candidate))
{
unavailableReasons[candidate] = ProviderListing.NotListedReason(candidate);
unavailableReasons[candidate] = UnavailableReason(candidate);
}
}

Expand Down Expand Up @@ -260,6 +260,13 @@ public static OpenSessionOutcome OpenSession(
null, decision.SelectedProvider!, fallbackChain);
}

/// <summary>
/// Why catalog plugin <paramref name="candidate"/> is not available. One this host cannot run
/// gets no install hint: <see cref="ProviderArgs.ProviderUnavailableHint"/> says why instead.
/// </summary>
internal static string UnavailableReason(ProviderId candidate) =>
ProviderListing.NotListedReason(candidate, installable: ProviderArgs.NotApplicableNote(candidate) is null);

internal static string FormatSelectionFailure(string? failureReason, ProviderId? preferredProvider)
{
var errorMsg = $"Provider selection failed: {failureReason ?? "Unknown reason"}";
Expand Down
20 changes: 16 additions & 4 deletions src/LinML.Cli/ProviderArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,7 @@ public static IReadOnlyDictionary<string, string> RegisterEpLibraries(
return null;
}

string? gap = Commands.SetupCommand.ArchitectureNote(
spec, System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant())
?? Commands.SetupCommand.MigraphXHardwareNote(spec.Id, Commands.SetupCommand.ProbeAmdRocm());
if (gap is not null)
if (NotApplicableNote(spec.Id) is { } gap)
{
return $"{GetShortName(spec.Id)} is not applicable on this host: {gap}.";
}
Expand All @@ -381,6 +378,21 @@ public static IReadOnlyDictionary<string, string> RegisterEpLibraries(
: null;
}

/// <summary>
/// Why this host lacks the hardware or architecture to run catalog plugin
/// <paramref name="provider"/> (so installing it would not help), or null when it could run here
/// or is not a catalog plugin.
/// </summary>
public static string? NotApplicableNote(ProviderId? provider)
{
var spec = LinML.Catalog.KnownPlugins.All.FirstOrDefault(p => p.Id == provider);
return spec is null
? null
: Commands.SetupCommand.ArchitectureNote(
spec, System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant())
?? Commands.SetupCommand.MigraphXHardwareNote(spec.Id, Commands.SetupCommand.ProbeAmdRocm());
}

/// <summary>
/// Registers, in this process, every catalog plugin that resolves on this host, so that
/// discovery, readiness and catalog status all see the same EP devices. A plugin that
Expand Down
10 changes: 7 additions & 3 deletions src/LinML.Runtime.Onnx/ProviderListing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ public static bool HasEpDevices(ProviderId provider) =>

/// <summary>
/// The reason a provider is not listed, worded for the state it is in: built-in, loaded but
/// without devices, failed to load (with ORT's message), or not loaded at all.
/// without devices, failed to load (with ORT's message), or not loaded at all. Pass
/// <paramref name="installable"/> false when the caller knows this host cannot run the
/// provider (no ROCm for MIGraphX, say): a plugin that is not loaded then gets no install hint.
/// </summary>
public static string NotListedReason(ProviderId provider)
public static string NotListedReason(ProviderId provider, bool installable = true)
{
var plugin = KnownPlugins.All.FirstOrDefault(p => p.Id == provider);
if (plugin is null)
Expand All @@ -51,7 +53,9 @@ public static string NotListedReason(ProviderId provider)
return $"Provider '{provider}' is installed but failed to register: {failure}";
}

return $"Provider '{provider}' is not loaded into this process. If the plugin is not installed, run 'linml catalog install {ShortName(provider)}' to vendor it.";
return installable
? $"Provider '{provider}' is not loaded into this process. If the plugin is not installed, run 'linml catalog install {ShortName(provider)}' to vendor it."
: $"Provider '{provider}' is not loaded into this process.";
}

// Matches the CLI's short names for these providers (ProviderArgs.GetShortName).
Expand Down
17 changes: 17 additions & 0 deletions tests/LinML.Cli.Tests/ProviderArgsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -519,4 +519,21 @@ public void ResolveEpLibraries_Wsl_ExplicitAndNonStockMappings_StayAuthoritative
[$"NvTensorRTRTXExecutionProvider={layout.Stock}"], ProviderId.TensorrtRtx, layout.Root);
Assert.Equal(layout.Stock, explicitArg["NvTensorRTRTXExecutionProvider"]);
}

// The selection failure for a provider this host cannot run must not tell the user to install
// it, whatever this process has registered: `run --require-provider migraphx` on a host without
// ROCm printed the not-applicable hint and an install hint from the listing reason.
[Fact]
public void UnavailableReason_ProviderTheHostCannotRun_HasNoInstallHint()
{
// MIGraphX without ROCm reaches the generic not-loaded reason; a plugin for another
// architecture (QNN on x64) is already worded as such and never had the hint.
if (Commands.SetupCommand.ProbeAmdRocm())
{
return;
}

Assert.NotNull(ProviderArgs.NotApplicableNote(ProviderId.MigraphX));
Assert.DoesNotContain("catalog install", InferencePipeline.UnavailableReason(ProviderId.MigraphX), StringComparison.Ordinal);
}
}
16 changes: 16 additions & 0 deletions tests/LinML.Runtime.Onnx.Tests/ProviderListingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,22 @@ public void NotListedReason_UnloadedPlugin_SuggestsInstallWithShortId()
Assert.Contains("linml catalog install openvino", reason, StringComparison.Ordinal);
}

[Fact]
public void NotListedReason_UnloadedPluginTheHostCannotRun_HasNoInstallHint()
{
const string openVinoLibrary = "libonnxruntime_providers_openvino_plugin.so";
if (!OpenVinoRunsOnThisHost || PluginLibraryRegistry.RegisteredLibraryFileNames.Contains(openVinoLibrary)
|| PluginLibraryRegistry.RegistrationFailure(openVinoLibrary) is not null)
{
return;
}

string reason = ProviderListing.NotListedReason(ProviderId.OpenVino, installable: false);

Assert.Contains("is not loaded into this process", reason, StringComparison.Ordinal);
Assert.DoesNotContain("catalog install", reason, StringComparison.Ordinal);
}

[Fact]
public void NotListedReason_PluginForAnotherArchitecture_SaysItWillNotRunHere()
{
Expand Down
Loading