Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 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
10 changes: 10 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ export enum TorchMirrorUrl {
NightlyCpu = 'https://download.pytorch.org/whl/nightly/cpu',
}

export type TorchUpdatePolicy = 'auto' | 'defer' | 'pinned';

export type TorchPinnedPackages = {
torch?: string;
torchaudio?: string;
torchvision?: string;
};
Comment on lines +184 to +190
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add brief JSDoc for new torch policy types.

These are exported public types; a short description helps consumers and aligns with the repo’s documentation expectations. As per coding guidelines, please add concise JSDoc.

✍️ Suggested doc additions
+/** How NVIDIA PyTorch updates should be handled. */
 export type TorchUpdatePolicy = 'auto' | 'defer' | 'pinned';

+/** Pinned NVIDIA torch package versions when updates are disabled. */
 export type TorchPinnedPackages = {
   torch?: string;
   torchaudio?: string;
   torchvision?: string;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export type TorchUpdatePolicy = 'auto' | 'defer' | 'pinned';
export type TorchPinnedPackages = {
torch?: string;
torchaudio?: string;
torchvision?: string;
};
/** How NVIDIA PyTorch updates should be handled. */
export type TorchUpdatePolicy = 'auto' | 'defer' | 'pinned';
/** Pinned NVIDIA torch package versions when updates are disabled. */
export type TorchPinnedPackages = {
torch?: string;
torchaudio?: string;
torchvision?: string;
};
🤖 Prompt for AI Agents
In `@src/constants.ts` around lines 184 - 190, Add concise JSDoc comments above
the exported types TorchUpdatePolicy and TorchPinnedPackages describing their
purpose and valid values/fields; for TorchUpdatePolicy document the allowed
string options ('auto', 'defer', 'pinned') and their behavior, and for
TorchPinnedPackages describe that it maps optional package names (torch,
torchaudio, torchvision) to version strings to pin specific versions. Keep
comments short, one-to-two sentences each and follow the existing JSDoc style in
the file.


/** Legacy NVIDIA torch mirror used by older installs (CUDA 12.9). */
export const LEGACY_NVIDIA_TORCH_MIRROR = 'https://download.pytorch.org/whl/cu129';

Expand Down Expand Up @@ -210,6 +218,8 @@ export const NVIDIA_TORCH_PACKAGES: string[] = [
`torchaudio==${NVIDIA_TORCH_VERSION}`,
`torchvision==${NVIDIA_TORCHVISION_VERSION}`,
];
/** Recommended NVIDIA torch package set key (torch/torchaudio/torchvision). */
export const NVIDIA_TORCH_RECOMMENDED_VERSION = `${NVIDIA_TORCH_VERSION}|${NVIDIA_TORCHVISION_VERSION}`;

/** The log files used by the desktop process. */
export enum LogFile {
Expand Down
119 changes: 116 additions & 3 deletions src/install/installationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ import { promisify } from 'node:util';

import { strictIpcMain as ipcMain } from '@/infrastructure/ipcChannels';

import { IPC_CHANNELS, InstallStage, ProgressStatus } from '../constants';
import {
IPC_CHANNELS,
InstallStage,
NVIDIA_TORCHVISION_VERSION,
NVIDIA_TORCH_RECOMMENDED_VERSION,
NVIDIA_TORCH_VERSION,
ProgressStatus,
} from '../constants';
import { PythonImportVerificationError } from '../infrastructure/pythonImportVerificationError';
import { useAppState } from '../main-process/appState';
import type { AppWindow } from '../main-process/appWindow';
Expand Down Expand Up @@ -91,7 +98,7 @@ export class InstallationManager implements HasTelemetry {
// Convert from old format
if (state === 'upgraded') installation.upgradeConfig();

// Install updated manager requirements
// Install updated requirements
if (installation.needsRequirementsUpdate) await this.updatePackages(installation);

// Resolve issues and re-run validation
Expand Down Expand Up @@ -382,14 +389,120 @@ export class InstallationManager implements HasTelemetry {
await installation.virtualEnvironment.installComfyUIRequirements(callbacks);
await installation.virtualEnvironment.installComfyUIManagerRequirements(callbacks);
await this.warnIfNvidiaDriverTooOld(installation);
await installation.virtualEnvironment.ensureRecommendedNvidiaTorch(callbacks);
await this.maybeUpdateNvidiaTorch(installation, callbacks);
await installation.validate();
} catch (error) {
log.error('Error auto-updating packages:', error);
await this.appWindow.loadPage('server-start');
}
}

private async maybeUpdateNvidiaTorch(installation: ComfyInstallation, callbacks: ProcessCallbacks): Promise<void> {
const virtualEnvironment = installation.virtualEnvironment;
if (virtualEnvironment.selectedDevice !== 'nvidia') return;

const config = useDesktopConfig();
const updatePolicy = config.get('torchUpdatePolicy');
if (updatePolicy === 'pinned') {
log.info('Skipping NVIDIA PyTorch update because updates are pinned.');
return;
}

const installedVersions = await virtualEnvironment.getInstalledTorchPackageVersions();
if (!installedVersions) {
log.warn('Skipping NVIDIA PyTorch update because installed versions could not be read.');
return;
}

const isOutOfDate = await virtualEnvironment.isNvidiaTorchOutOfDate(installedVersions);
if (!isOutOfDate) return;

const recommendedVersion = NVIDIA_TORCH_RECOMMENDED_VERSION;
const lastPromptedVersion = config.get('torchLastPromptedVersion');

if (config.get('torchOutOfDateRecommendedVersion') !== recommendedVersion) {
config.set('torchOutOfDateRecommendedVersion', recommendedVersion);
config.set('torchOutOfDatePackages', installedVersions);
}

if (updatePolicy === 'defer' && lastPromptedVersion === recommendedVersion) {
log.info('Skipping NVIDIA PyTorch update because updates are deferred for this version.');
return;
}

const autoPolicy = updatePolicy === undefined || updatePolicy === 'auto';
const updateApproved = autoPolicy && lastPromptedVersion === recommendedVersion;

if (!updateApproved) {
const currentTorch = installedVersions.torch ?? 'unknown';
const currentTorchaudio = installedVersions.torchaudio ?? 'unknown';
const currentTorchvision = installedVersions.torchvision ?? 'unknown';

const { response } = await this.appWindow.showMessageBox({
type: 'question',
title: 'Update PyTorch?',
message:
'Your NVIDIA PyTorch build is out of date. We can update to the recommended build for performance improvements. This may change memory use and/or compatibility with custom nodes',
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing period at the end of the sentence.

Suggested change
'Your NVIDIA PyTorch build is out of date. We can update to the recommended build for performance improvements. This may change memory use and/or compatibility with custom nodes',
'Your NVIDIA PyTorch build is out of date. We can update to the recommended build for performance improvements. This may change memory use and/or compatibility with custom nodes.',

Copilot uses AI. Check for mistakes.
detail: [
`Current: torch ${currentTorch}, torchaudio ${currentTorchaudio}, torchvision ${currentTorchvision}`,
`Recommended: torch ${NVIDIA_TORCH_VERSION}, torchaudio ${NVIDIA_TORCH_VERSION}, torchvision ${NVIDIA_TORCHVISION_VERSION}`,
].join('\n'),
buttons: ['Update PyTorch', 'Ask again later', 'Silence until next version', 'Silence forever'],
defaultId: 0,
cancelId: 1,
});

switch (response) {
case 1:
log.info('Deferring NVIDIA PyTorch update prompt.');
return;
case 2:
config.set('torchLastPromptedVersion', recommendedVersion);
config.set('torchUpdatePolicy', 'defer');
config.delete('torchPinnedPackages');
virtualEnvironment.updateTorchUpdatePolicy('defer', undefined, recommendedVersion);
return;
case 3:
config.set('torchLastPromptedVersion', recommendedVersion);
config.set('torchUpdatePolicy', 'pinned');
config.set('torchPinnedPackages', installedVersions);
virtualEnvironment.updateTorchUpdatePolicy('pinned', installedVersions, recommendedVersion);
return;
default:
config.set('torchLastPromptedVersion', recommendedVersion);
config.set('torchUpdatePolicy', 'auto');
config.delete('torchPinnedPackages');
config.delete('torchUpdateFailureSilencedVersion');
virtualEnvironment.updateTorchUpdatePolicy('auto', undefined, recommendedVersion);
}
} else {
virtualEnvironment.updateTorchUpdatePolicy('auto', undefined, recommendedVersion);
}

try {
await virtualEnvironment.ensureRecommendedNvidiaTorch(callbacks);
config.delete('torchUpdateFailureSilencedVersion');
} catch (error) {
log.error('Error updating NVIDIA PyTorch packages:', error);
if (config.get('torchUpdateFailureSilencedVersion') === recommendedVersion) return;

const { response } = await this.appWindow.showMessageBox({
type: 'warning',
title: 'PyTorch update failed',
message:
'We could not install the recommended NVIDIA PyTorch build. This may be because your configured torch mirror does not provide it.',
detail: 'We will retry the update on each startup.',
buttons: ['OK', "Don't show again"],
defaultId: 0,
cancelId: 0,
});

if (response === 1) {
config.set('torchUpdateFailureSilencedVersion', recommendedVersion);
}
}
}
Comment on lines +404 to +512
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "isUsingCustomTorchMirror|ensureRecommendedNvidiaTorch|torchMirror" src/ -t ts

Repository: Comfy-Org/desktop

Length of output: 2404


🏁 Script executed:

sed -n '694,740p' src/virtualEnvironment.ts

Repository: Comfy-Org/desktop

Length of output: 2001


🏁 Script executed:

grep -n "isUsingCustomTorchMirror" src/install/installationManager.ts

Repository: Comfy-Org/desktop

Length of output: 43


🏁 Script executed:

grep -rn "isUsingCustomTorchMirror" src/ -t ts

Repository: Comfy-Org/desktop

Length of output: 171


🏁 Script executed:

rg -n "isUsingCustomTorchMirror" src/

Repository: Comfy-Org/desktop

Length of output: 130


Add early return for custom torch mirrors.

The method isUsingCustomTorchMirror() exists but is unused in the torch update flow. Without a guard at the start of maybeUpdateNvidiaTorch(), users with custom mirrors will still see update prompts and attempts, contradicting the PR objective to skip updates when a custom mirror is configured.

Add the check after the device check:

if (virtualEnvironment.isUsingCustomTorchMirror()) {
  log.info('Skipping NVIDIA PyTorch update because a custom torch mirror is configured.');
  return;
}
🤖 Prompt for AI Agents
In `@src/install/installationManager.ts` around lines 400 - 504, The
maybeUpdateNvidiaTorch function lacks a guard for custom torch mirrors; after
the existing device check (virtualEnvironment.selectedDevice !== 'nvidia') add a
check using virtualEnvironment.isUsingCustomTorchMirror() and if true log a
message like "Skipping NVIDIA PyTorch update because a custom torch mirror is
configured." and return; update references: maybeUpdateNvidiaTorch,
virtualEnvironment.isUsingCustomTorchMirror(), and keep the rest of the flow
unchanged so prompts and installs are skipped when a custom mirror is set.


/**
* Warns the user if their NVIDIA driver is too old for the required CUDA build.
* @param installation The current installation.
Expand Down
3 changes: 3 additions & 0 deletions src/main-process/comfyInstallation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ export class ComfyInstallation {
pythonMirror: useComfySettings().get('Comfy-Desktop.UV.PythonInstallMirror'),
pypiMirror: useComfySettings().get('Comfy-Desktop.UV.PypiInstallMirror'),
torchMirror: useComfySettings().get('Comfy-Desktop.UV.TorchInstallMirror'),
torchUpdatePolicy: useDesktopConfig().get('torchUpdatePolicy'),
torchPinnedPackages: useDesktopConfig().get('torchPinnedPackages'),
torchUpdateDecisionVersion: useDesktopConfig().get('torchLastPromptedVersion'),
});
}

Expand Down
13 changes: 13 additions & 0 deletions src/store/desktopSettings.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { TorchPinnedPackages, TorchUpdatePolicy } from '../constants';
import type { GpuType, TorchDeviceType } from '../preload';

export type DesktopInstallState = 'started' | 'installed' | 'upgraded';
Expand Down Expand Up @@ -35,4 +36,16 @@ export type DesktopSettings = {
versionConsentedMetrics?: string;
/** Whether the user has generated an image successfully. */
hasGeneratedSuccessfully?: boolean;
/** How to handle NVIDIA PyTorch updates. */
torchUpdatePolicy?: TorchUpdatePolicy;
/** The pinned NVIDIA torch package versions when updates are disabled. */
torchPinnedPackages?: TorchPinnedPackages;
/** The recommended NVIDIA torch version tied to the current update decision. */
torchLastPromptedVersion?: string;
/** The recommended NVIDIA torch version whose update failure prompt is suppressed. */
torchUpdateFailureSilencedVersion?: string;
/** The recommended NVIDIA torch version recorded when we first detected an out-of-date torch install. */
torchOutOfDateRecommendedVersion?: string;
/** The torch package versions recorded when we first detected an out-of-date torch install. */
torchOutOfDatePackages?: TorchPinnedPackages;
};
79 changes: 69 additions & 10 deletions src/virtualEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ import {
LEGACY_NVIDIA_TORCH_MIRROR,
NVIDIA_TORCHVISION_VERSION,
NVIDIA_TORCH_PACKAGES,
NVIDIA_TORCH_RECOMMENDED_VERSION,
NVIDIA_TORCH_VERSION,
TorchMirrorUrl,
TorchPinnedPackages,
TorchUpdatePolicy,
} from './constants';
import { PythonImportVerificationError } from './infrastructure/pythonImportVerificationError';
import { useAppState } from './main-process/appState';
Expand Down Expand Up @@ -89,13 +92,7 @@ function getPipInstallArgs(config: PipInstallConfig): string[] {
return installArgs;
}

/**
* Returns the default torch mirror for the given device.
* @param device The device type
* @returns The default torch mirror
*/
function getDefaultTorchMirror(device: TorchDeviceType): string {
log.debug('Falling back to default torch mirror');
function getDeviceDefaultTorchMirror(device: TorchDeviceType): string {
switch (device) {
case 'mps':
return TorchMirrorUrl.NightlyCpu;
Expand Down Expand Up @@ -140,6 +137,9 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
readonly pythonMirror?: string;
readonly pypiMirror?: string;
readonly torchMirror?: string;
torchUpdatePolicy?: TorchUpdatePolicy;
torchPinnedPackages?: TorchPinnedPackages;
torchUpdateDecisionVersion?: string;
Comment on lines +143 to +145
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Public mutable properties warrant careful consideration.

These properties are public and mutable, meaning external code can modify them directly. While this aligns with the current usage via updateTorchUpdatePolicy, consider whether these should be readonly with state changes only through the setter method for better encapsulation.

🤖 Prompt for AI Agents
In `@src/virtualEnvironment.ts` around lines 143 - 145, The three public mutable
properties torchUpdatePolicy, torchPinnedPackages, and
torchUpdateDecisionVersion should not be directly writable; make them private
(e.g., _torchUpdatePolicy, _torchPinnedPackages, _torchUpdateDecisionVersion)
and expose read-only accessors (getters) while ensuring all mutations go through
the existing updateTorchUpdatePolicy method; update references inside the class
to use the private fields and replace external direct assignments with the
setter method to preserve encapsulation.

uvPty: pty.IPty | undefined;

/** The environment variables to set for uv. */
Expand Down Expand Up @@ -184,13 +184,19 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
pythonMirror,
pypiMirror,
torchMirror,
torchUpdatePolicy,
torchPinnedPackages,
torchUpdateDecisionVersion,
}: {
telemetry: ITelemetry;
selectedDevice?: TorchDeviceType;
pythonVersion?: string;
pythonMirror?: string;
pypiMirror?: string;
torchMirror?: string;
torchUpdatePolicy?: TorchUpdatePolicy;
torchPinnedPackages?: TorchPinnedPackages;
torchUpdateDecisionVersion?: string;
}
) {
this.basePath = basePath;
Expand All @@ -200,6 +206,9 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
this.pythonMirror = pythonMirror;
this.pypiMirror = pypiMirror;
this.torchMirror = fixDeviceMirrorMismatch(selectedDevice!, torchMirror);
this.torchUpdatePolicy = torchUpdatePolicy;
this.torchPinnedPackages = torchPinnedPackages;
this.torchUpdateDecisionVersion = torchUpdateDecisionVersion;

// uv defaults to .venv
this.venvPath = path.join(basePath, '.venv');
Expand Down Expand Up @@ -263,6 +272,34 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
return primary;
}

updateTorchUpdatePolicy(
policy: TorchUpdatePolicy | undefined,
pinnedPackages?: TorchPinnedPackages,
decisionVersion?: string
) {
this.torchUpdatePolicy = policy;
if (pinnedPackages !== undefined) {
this.torchPinnedPackages = pinnedPackages;
} else if (policy !== 'pinned') {
this.torchPinnedPackages = undefined;
}
if (decisionVersion !== undefined) {
this.torchUpdateDecisionVersion = decisionVersion;
}
}
Comment on lines +287 to +301
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's check the file and locate the class definition and the method in question
head -300 src/virtualEnvironment.ts | tail -100

Repository: Comfy-Org/desktop

Length of output: 3646


🏁 Script executed:

# Also check the beginning of the file to see class export
head -50 src/virtualEnvironment.ts

Repository: Comfy-Org/desktop

Length of output: 1725


🏁 Script executed:

# Check if the class is exported
grep -n "export.*class\|class.*{" src/virtualEnvironment.ts | head -5

Repository: Comfy-Org/desktop

Length of output: 138


🏁 Script executed:

# Search for all public methods in VirtualEnvironment class to check consistency
rg -A 2 "^\s+(async\s+)?[a-zA-Z]+\(" src/virtualEnvironment.ts | head -80

Repository: Comfy-Org/desktop

Length of output: 2875


🏁 Script executed:

# More targeted: find methods and check for return type annotations
ast-grep --pattern 'class VirtualEnvironment {
  $$$
  $_($_) {
    $$$
  }
  $$$
}'

Repository: Comfy-Org/desktop

Length of output: 43


🏁 Script executed:

# Let's get a cleaner view of the class methods and their signatures
sed -n '124,400p' src/virtualEnvironment.ts | grep -E "^\s+(async\s+)?[a-zA-Z]+.*\(" | head -20

Repository: Comfy-Org/desktop

Length of output: 1222


Add explicit return type to this public method.

The updateTorchUpdatePolicy() method is missing an explicit return type annotation. All other public methods in the VirtualEnvironment class have explicit return types; this method should as well to comply with the coding guidelines.

🔧 Proposed fix
   updateTorchUpdatePolicy(
     policy: TorchUpdatePolicy | undefined,
     pinnedPackages?: TorchPinnedPackages,
     decisionVersion?: string
-  ) {
+  ): void {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
updateTorchUpdatePolicy(
policy: TorchUpdatePolicy | undefined,
pinnedPackages?: TorchPinnedPackages,
decisionVersion?: string
) {
this.torchUpdatePolicy = policy;
if (pinnedPackages !== undefined) {
this.torchPinnedPackages = pinnedPackages;
} else if (policy !== 'pinned') {
this.torchPinnedPackages = undefined;
}
if (decisionVersion !== undefined) {
this.torchUpdateDecisionVersion = decisionVersion;
}
}
updateTorchUpdatePolicy(
policy: TorchUpdatePolicy | undefined,
pinnedPackages?: TorchPinnedPackages,
decisionVersion?: string
): void {
this.torchUpdatePolicy = policy;
if (pinnedPackages !== undefined) {
this.torchPinnedPackages = pinnedPackages;
} else if (policy !== 'pinned') {
this.torchPinnedPackages = undefined;
}
if (decisionVersion !== undefined) {
this.torchUpdateDecisionVersion = decisionVersion;
}
}
🤖 Prompt for AI Agents
In `@src/virtualEnvironment.ts` around lines 275 - 289, The public method
updateTorchUpdatePolicy in class VirtualEnvironment lacks an explicit return
type; add the explicit return type `: void` to the method signature of
updateTorchUpdatePolicy(policy: TorchUpdatePolicy | undefined, pinnedPackages?:
TorchPinnedPackages, decisionVersion?: string) to match other public methods and
coding guidelines so the signature clearly indicates it returns nothing.


isUsingCustomTorchMirror(): boolean {
if (!this.torchMirror) return false;
return this.torchMirror !== getDeviceDefaultTorchMirror(this.selectedDevice);
}

private shouldSkipNvidiaTorchUpgrade(): boolean {
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The isUsingCustomTorchMirror method is defined but never used in the codebase. According to the PR description, updates should be skipped when using a custom mirror, but this check is not implemented in maybeUpdateNvidiaTorch or anywhere else. Either this method should be integrated into the update flow, or it should be removed if it's not part of the intended feature set.

Suggested change
private shouldSkipNvidiaTorchUpgrade(): boolean {
private shouldSkipNvidiaTorchUpgrade(): boolean {
if (this.isUsingCustomTorchMirror()) return true;

Copilot uses AI. Check for mistakes.
if (this.torchUpdatePolicy === 'pinned') return true;
if (this.torchUpdatePolicy === 'defer' && this.torchUpdateDecisionVersion === NVIDIA_TORCH_RECOMMENDED_VERSION)
return true;
return false;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for call sites of isUsingCustomTorchMirror and torchUpdatePolicy
echo "=== Looking for isUsingCustomTorchMirror usage ==="
rg -nP --type=ts -C2 'isUsingCustomTorchMirror\s*\('

echo ""
echo "=== Looking for shouldSkipNvidiaTorchUpgrade usage ==="
rg -nP --type=ts -C2 'shouldSkipNvidiaTorchUpgrade\s*\('

echo ""
echo "=== Looking for NVIDIA torch upgrade decision points ==="
rg -nP --type=ts -C3 'torchUpdatePolicy|shouldSkipNvidia' | head -100

Repository: Comfy-Org/desktop

Length of output: 9079


🏁 Script executed:

#!/bin/bash
# Get full context of ensureRecommendedNvidiaTorch method
echo "=== ensureRecommendedNvidiaTorch implementation ==="
sed -n '694,750p' src/virtualEnvironment.ts

echo ""
echo "=== needsNvidiaTorchUpgrade implementation ==="
sed -n '1041,1100p' src/virtualEnvironment.ts

echo ""
echo "=== Check if custom mirror is checked anywhere in upgrade flow ==="
rg -nP --type=ts 'isUsingCustomTorchMirror' src/virtualEnvironment.ts

Repository: Comfy-Org/desktop

Length of output: 4652


🏁 Script executed:

#!/bin/bash
# Check if tests verify custom mirror blocking NVIDIA upgrades
echo "=== Test coverage for custom mirror and NVIDIA upgrades ==="
sed -n '300,330p' tests/unit/virtualEnvironment.test.ts

echo ""
echo "=== Search for test cases related to shouldSkipNvidiaTorchUpgrade ==="
rg -nP --type=ts 'shouldSkipNvidiaTorchUpgrade|ensureRecommendedNvidiaTorch' tests/

echo ""
echo "=== Check if custom mirror is tested with upgrade logic ==="
rg -nP --type=ts -B5 -A5 'isUsingCustomTorchMirror.*true' tests/ | head -50

Repository: Comfy-Org/desktop

Length of output: 5535


🏁 Script executed:

#!/bin/bash
# Check if custom mirror skip is mentioned in any documentation or PR-related files
echo "=== Search for PR objectives or documentation about custom mirror ==="
find . -type f \( -name "*.md" -o -name "*.txt" \) | head -20 | xargs grep -l "custom.*mirror\|torch.*mirror" 2>/dev/null

echo ""
echo "=== Look for any references to the PR or CHANGELOG ==="
git log --oneline --all -S "isUsingCustomTorchMirror" | head -10

echo ""
echo "=== Check if shouldSkipNvidiaTorchUpgrade should check custom mirror ==="
echo "Looking for all places shouldSkipNvidiaTorchUpgrade is called:"
rg -nP --type=ts 'shouldSkipNvidiaTorchUpgrade' src/

Repository: Comfy-Org/desktop

Length of output: 595


🏁 Script executed:

#!/bin/bash
# Check the full implementation and see if custom mirror should prevent upgrades
echo "=== Verify the relationship: custom mirror usage context ==="
sed -n '708,720p' src/virtualEnvironment.ts

echo ""
echo "=== Check if installationManager has any custom mirror checks ==="
rg -nP --type=ts 'isUsingCustomTorchMirror|torchMirror' src/install/installationManager.ts | head -20

echo ""
echo "=== Summary: All places where NVIDIA torch updates are gated ==="
rg -nP --type=ts -B2 'ensureRecommendedNvidiaTorch|needsNvidiaTorchUpgrade' src/ --type-list | grep -E '\.(ts|tsx):' | head -30

Repository: Comfy-Org/desktop

Length of output: 1268


🏁 Script executed:

#!/bin/bash
# Final verification: confirm custom mirror skip is NOT implemented
echo "=== Direct check: Does shouldSkipNvidiaTorchUpgrade call isUsingCustomTorchMirror? ==="
sed -n '296,301p' src/virtualEnvironment.ts

echo ""
echo "=== Do ensureRecommendedNvidiaTorch or needsNvidiaTorchUpgrade check custom mirror? ==="
grep -n "isUsingCustomTorchMirror" src/virtualEnvironment.ts

echo ""
echo "=== Log message context at upgrade skip point ==="
sed -n '694,700p' src/virtualEnvironment.ts

Repository: Comfy-Org/desktop

Length of output: 893


Custom mirror skip is not enforced in NVIDIA upgrade paths.

The isUsingCustomTorchMirror() helper exists but is unused. The shouldSkipNvidiaTorchUpgrade() method only checks policy ('pinned' or 'defer') and does not consult the custom mirror check. Both ensureRecommendedNvidiaTorch() and needsNvidiaTorchUpgrade() rely solely on this incomplete gate, meaning custom mirror configurations do not prevent NVIDIA PyTorch upgrades. Add isUsingCustomTorchMirror() to the skip logic in shouldSkipNvidiaTorchUpgrade().

🤖 Prompt for AI Agents
In `@src/virtualEnvironment.ts` around lines 291 - 301, The skip logic in
shouldSkipNvidiaTorchUpgrade() must also honor custom mirrors: update
shouldSkipNvidiaTorchUpgrade() to return true if isUsingCustomTorchMirror() is
true in addition to the existing checks (torchUpdatePolicy === 'pinned' or the
'defer' + torchUpdateDecisionVersion === NVIDIA_TORCH_RECOMMENDED_VERSION case)
so that ensureRecommendedNvidiaTorch() and needsNvidiaTorchUpgrade() will not
trigger NVIDIA PyTorch upgrades when a custom torchMirror (checked via
isUsingCustomTorchMirror(), which compares this.torchMirror against
getDeviceDefaultTorchMirror(this.selectedDevice)) is configured.


public async create(callbacks?: ProcessCallbacks): Promise<void> {
try {
await this.createEnvironment(callbacks);
Expand Down Expand Up @@ -629,7 +666,11 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
return;
}

const torchMirror = this.torchMirror || getDefaultTorchMirror(this.selectedDevice);
let torchMirror = this.torchMirror;
if (!torchMirror) {
log.info('Falling back to default torch mirror');
torchMirror = getDeviceDefaultTorchMirror(this.selectedDevice);
}
const config: PipInstallConfig = {
packages: ['torch', 'torchvision', 'torchaudio'],
indexUrl: torchMirror,
Expand All @@ -652,14 +693,22 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
*/
async ensureRecommendedNvidiaTorch(callbacks?: ProcessCallbacks): Promise<void> {
if (this.selectedDevice !== 'nvidia') return;
if (this.shouldSkipNvidiaTorchUpgrade()) {
log.info('Skipping NVIDIA PyTorch upgrade due to pinned policy or deferred updates.');
return;
}

const installedVersions = await this.getInstalledTorchPackageVersions();
if (installedVersions && this.meetsMinimumNvidiaTorchVersions(installedVersions)) {
log.info('NVIDIA PyTorch packages already satisfy minimum recommended versions.', installedVersions);
return;
}

const torchMirror = this.torchMirror || getDefaultTorchMirror(this.selectedDevice);
let torchMirror = this.torchMirror;
if (!torchMirror) {
log.info('Falling back to default torch mirror');
torchMirror = getDeviceDefaultTorchMirror(this.selectedDevice);
}
const config: PipInstallConfig = {
packages: NVIDIA_TORCH_PACKAGES,
indexUrl: torchMirror,
Expand Down Expand Up @@ -695,7 +744,7 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
* Reads installed torch package versions using `uv pip list --format=json`.
* @returns The torch package versions when available, otherwise `undefined`.
*/
private async getInstalledTorchPackageVersions(): Promise<TorchPackageVersions | undefined> {
async getInstalledTorchPackageVersions(): Promise<TorchPackageVersions | undefined> {
let stdout = '';
let stderr = '';
const callbacks: ProcessCallbacks = {
Expand Down Expand Up @@ -758,6 +807,15 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
return versions;
}

async isNvidiaTorchOutOfDate(installedVersions?: TorchPackageVersions): Promise<boolean> {
if (this.selectedDevice !== 'nvidia') return false;

const resolvedVersions = installedVersions ?? (await this.getInstalledTorchPackageVersions());
if (!resolvedVersions) return false;

return !this.meetsMinimumNvidiaTorchVersions(resolvedVersions);
}

/**
* Installs AMD ROCm SDK packages on Windows.
* @param callbacks The callbacks to use for the command.
Expand Down Expand Up @@ -982,6 +1040,7 @@ export class VirtualEnvironment implements HasTelemetry, PythonExecutor {
*/
private async needsNvidiaTorchUpgrade(): Promise<boolean> {
if (this.selectedDevice !== 'nvidia') return false;
if (this.shouldSkipNvidiaTorchUpgrade()) return false;

const installedVersions = await this.getInstalledTorchPackageVersions();
if (!installedVersions) {
Expand Down
Loading
Loading