Skip to content
Open
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
14 changes: 13 additions & 1 deletion src/main/install-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { DEFAULT_ENV } from '@/lib/pty-utils';
import { withResultAsync } from '@/lib/result';
import { SimpleLogger } from '@/lib/simple-logger';
import { FIRST_RUN_MARKER_FILENAME } from '@/main/constants';
import { store } from '@/main/store';
import { getInstallationDetails, getTorchPlatform, getUVExecutablePath, isDirectory, isFile } from '@/main/util';
import { getPins } from '@/shared/pins';
import type {
Expand Down Expand Up @@ -188,7 +189,13 @@ export class InstallManager {
}

const pythonVersion = pinsResult.value.python;
const torchIndexUrl = pinsResult.value.torchIndexUrl[systemPlatform][torchPlatform];
const pinnedTorchIndexUrl = pinsResult.value.torchIndexUrl[systemPlatform][torchPlatform];

// An optional user-provided index URL overrides the pinned one. This is an advanced escape hatch for cases the pins
// can't cover (e.g. older Nvidia GPUs needing a different CUDA build, or AMD on Windows where there is no pinned
// index). The user is responsible for providing a working URL - an invalid one will break the install.
const customTorchIndexUrl = store.get('customTorchIndexUrl')?.trim() || undefined;
const torchIndexUrl = customTorchIndexUrl ?? pinnedTorchIndexUrl;

const installationDetails = await getInstallationDetails(location);

Expand Down Expand Up @@ -218,6 +225,11 @@ export class InstallManager {
this.log.info(`- GPU type: ${gpuType}\r\n`);
this.log.info(`- Torch platform: ${torchPlatform}\r\n`);
this.log.info(`- Using torch index: ${torchIndexUrl ?? 'default'}\r\n`);
if (customTorchIndexUrl) {
this.log.info(
c.magenta(`- Custom torch index URL override is active (set in Settings): ${customTorchIndexUrl}\r\n`)
);
}

if (repair) {
this.log.info(c.magenta('Repair mode enabled:\r\n'));
Expand Down
10 changes: 9 additions & 1 deletion src/main/main-process-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ export class MainProcessManager {
this.sendToWindow('store:changed', data);
});
this.ipc.handle('store:get-key', (_, key) => this.store.get(key));
this.ipc.handle('store:set-key', (_, key, value) => this.store.set(key, value));
this.ipc.handle('store:set-key', (_, key, value) => {
// electron-store throws if you `set` a key to `undefined` - you must `delete` it instead. We treat setting a key
// to `undefined` as a request to clear it.
if (value === undefined) {
this.store.delete(key);
} else {
this.store.set(key, value);
}
});
this.ipc.handle('store:get', (_) => this.store.store);
this.ipc.handle('store:set', (_, data) => {
this.store.store = data;
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/features/SettingsModal/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { useStore } from '@nanostores/react';
import { memo, useCallback } from 'react';

import { SettingsModalCustomTorchIndexUrl } from '@/renderer/features/SettingsModal/SettingsModalCustomTorchIndexUrl';
import { SettingsModalInvokeNotifyForPrereleaseUpdates } from '@/renderer/features/SettingsModal/SettingsModalInvokeNotifyForPrereleaseUpdates';
import { SettingsModalInvokeServerMode } from '@/renderer/features/SettingsModal/SettingsModalInvokeServerMode';
import { SettingsModalOptInToLauncherPrereleases } from '@/renderer/features/SettingsModal/SettingsModalOptInToLauncherPrereleases';
Expand All @@ -39,6 +40,8 @@ export const SettingsModal = memo(() => {
<SettingsModalInvokeNotifyForPrereleaseUpdates />
<Divider />
<SettingsModalOptInToLauncherPrereleases />
<Divider />
<SettingsModalCustomTorchIndexUrl />
</ModalBody>
<ModalFooter pt={16}>
<SettingsModalResetButton />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Flex, FormControl, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { memo, useCallback, useEffect, useState } from 'react';

import { persistedStoreApi } from '@/renderer/services/store';

export const SettingsModalCustomTorchIndexUrl = memo(() => {
const { customTorchIndexUrl } = useStore(persistedStoreApi.$atom);
// Keep a local value so typing doesn't round-trip to the store on every keystroke. We persist on blur.
const [value, setValue] = useState(customTorchIndexUrl ?? '');

useEffect(() => {
setValue(customTorchIndexUrl ?? '');
}, [customTorchIndexUrl]);

const onChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
}, []);

const onBlur = useCallback(() => {
const trimmed = value.trim();
persistedStoreApi.setKey('customTorchIndexUrl', trimmed === '' ? undefined : trimmed);
}, [value]);

return (
<FormControl orientation="vertical">
<Flex w="full" alignItems="center" justifyContent="space-between">
<FormLabel>Custom PyTorch Index URL</FormLabel>
</Flex>
<Input value={value} onChange={onChange} onBlur={onBlur} placeholder="https://download.pytorch.org/whl/cu126" />
<FormHelperText>
Advanced: overrides the PyTorch index URL from Invoke&apos;s pins for all installs and updates. Use this if the
default build does not support your GPU (e.g. older Nvidia cards) or for AMD on Windows, where no index is
provided. The torch version is still pinned by Invoke &ndash; this only changes the build/index. If you set
this, you are on your own: an invalid URL will break installation. Leave empty to use Invoke&apos;s default.
</FormHelperText>
</FormControl>
);
});
SettingsModalCustomTorchIndexUrl.displayName = 'SettingsModalCustomTorchIndexUrl';
12 changes: 12 additions & 0 deletions src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ export type StoreData = {
launcherWindowProps?: WindowProps;
appWindowProps?: WindowProps;
optInToLauncherPrereleases: boolean;
/**
* An optional user-provided PyTorch index URL. When set, it overrides the index URL derived from Invoke's pins for
* all installs and updates.
*
* This is an advanced escape hatch for cases the pins can't cover, e.g. older Nvidia GPUs that need a different CUDA
* build, or AMD on Windows (where the pins provide no index at all). Note that this only changes the index/build - the
* torch *version* is still pinned by the invokeai package.
*/
customTorchIndexUrl?: string;
};

// The electron store uses JSON schema to validate its data.
Expand Down Expand Up @@ -97,6 +106,9 @@ export const schema: Schema<StoreData> = {
type: 'boolean',
default: false,
},
customTorchIndexUrl: {
type: 'string',
},
};

/**
Expand Down
Loading