mirror of
https://github.com/immich-app/static-pages.git
synced 2026-09-30 21:27:54 +08:00
feat(builder): support remote machine learning (#771)
This commit is contained in:
@@ -245,6 +245,16 @@ describe('buildCompose', () => {
|
||||
expect(spec.services['immich-server'].depends_on).toEqual(['redis']);
|
||||
});
|
||||
|
||||
it('remote machine learning drops the service, its model cache, and any hwaccel fragment', () => {
|
||||
const config = structuredClone(DEFAULT_CONFIG);
|
||||
config.machineLearning.external = true;
|
||||
config.hwaccel.ml = 'cuda';
|
||||
const spec = parse(buildCompose(config, VERSION));
|
||||
expect(spec.services['immich-machine-learning']).toBeUndefined();
|
||||
expect(spec.volumes).toBeUndefined();
|
||||
expect(buildCompose(config, VERSION)).not.toContain('driver: nvidia');
|
||||
});
|
||||
|
||||
it('external Redis drops the redis service and sets non-default REDIS_ vars', () => {
|
||||
const config = structuredClone(DEFAULT_CONFIG);
|
||||
config.redis.external = true;
|
||||
|
||||
@@ -122,6 +122,23 @@ const build = (config: ImmichConfig, version: string): { spec: ComposeFile; fiel
|
||||
);
|
||||
const overrideKeys = overrides.map(({ key }) => key);
|
||||
|
||||
const bundledMl = !config.machineLearning.external;
|
||||
|
||||
const mlService: Record<string, ComposeService> = bundledMl
|
||||
? {
|
||||
'immich-machine-learning': deepmerge<ComposeService>(
|
||||
{
|
||||
container_name: containerName('immich_machine_learning'),
|
||||
image: IMAGES.machineLearning(version + ML_BACKENDS[config.hwaccel.ml].tag),
|
||||
volumes: rootlessVolumes('immich-machine-learning') ?? [`${NamedVolume.ModelCache}:/cache`],
|
||||
restart: 'always',
|
||||
healthcheck: { disable: false },
|
||||
},
|
||||
ML_BACKENDS[config.hwaccel.ml].fragment,
|
||||
),
|
||||
}
|
||||
: {};
|
||||
|
||||
const services: Record<string, ComposeService> = {
|
||||
'immich-server': deepmerge<ComposeService>(
|
||||
{
|
||||
@@ -142,16 +159,7 @@ const build = (config: ImmichConfig, version: string): { spec: ComposeFile; fiel
|
||||
},
|
||||
TRANSCODE_BACKENDS[config.hwaccel.transcoding].fragment,
|
||||
),
|
||||
'immich-machine-learning': deepmerge<ComposeService>(
|
||||
{
|
||||
container_name: containerName('immich_machine_learning'),
|
||||
image: IMAGES.machineLearning(version + ML_BACKENDS[config.hwaccel.ml].tag),
|
||||
volumes: rootlessVolumes('immich-machine-learning') ?? [`${NamedVolume.ModelCache}:/cache`],
|
||||
restart: 'always',
|
||||
healthcheck: { disable: false },
|
||||
},
|
||||
ML_BACKENDS[config.hwaccel.ml].fragment,
|
||||
),
|
||||
...mlService,
|
||||
...backingServices,
|
||||
};
|
||||
|
||||
@@ -194,6 +202,7 @@ const build = (config: ImmichConfig, version: string): { spec: ComposeFile; fiel
|
||||
return value === undefined || value === '' ? [] : [['services', 'immich-server', 'environment', key]];
|
||||
},
|
||||
bundledDatabase: !config.database.external,
|
||||
bundledMl,
|
||||
databaseVolume,
|
||||
network: externalNetwork
|
||||
? [
|
||||
@@ -217,11 +226,13 @@ const build = (config: ImmichConfig, version: string): { spec: ComposeFile; fiel
|
||||
'immich-server',
|
||||
key,
|
||||
])
|
||||
: Object.keys(ML_BACKENDS[config.hwaccel.ml].fragment).map((key) => [
|
||||
'services',
|
||||
'immich-machine-learning',
|
||||
key,
|
||||
]),
|
||||
: bundledMl
|
||||
? Object.keys(ML_BACKENDS[config.hwaccel.ml].fragment).map((key) => [
|
||||
'services',
|
||||
'immich-machine-learning',
|
||||
key,
|
||||
])
|
||||
: [],
|
||||
};
|
||||
|
||||
const fields: FieldPaths = Object.fromEntries(
|
||||
|
||||
@@ -54,6 +54,9 @@ const immichConfigSchema = z.object({
|
||||
transcoding: transcodeAccelSchema,
|
||||
ml: mlAccelSchema,
|
||||
}),
|
||||
machineLearning: z.object({
|
||||
external: z.boolean(),
|
||||
}),
|
||||
storage: z.object({
|
||||
uploadLocation: required('Upload location is required.'),
|
||||
customFolders: z.boolean(),
|
||||
@@ -90,6 +93,7 @@ export type OutputContext = {
|
||||
services: string[];
|
||||
env: (key: string) => YamlPath[];
|
||||
bundledDatabase: boolean;
|
||||
bundledMl: boolean;
|
||||
databaseVolume: YamlPath[];
|
||||
network: YamlPath[];
|
||||
rootless: YamlPath[];
|
||||
@@ -110,10 +114,7 @@ const DATABASE = ['services', 'database'];
|
||||
export const FIELDS = {
|
||||
version: {
|
||||
config: [],
|
||||
output: ({ server, ml }) => [
|
||||
[...server, 'image'],
|
||||
[...ml, 'image'],
|
||||
],
|
||||
output: ({ server, ml, bundledMl }) => [[...server, 'image'], ...(bundledMl ? [[...ml, 'image']] : [])],
|
||||
},
|
||||
timezone: { config: ['timezone'], output: ({ env }) => env('TZ') },
|
||||
port: { config: ['port'], advanced: true, output: ({ server }) => [[...server, 'ports', 0]] },
|
||||
@@ -129,7 +130,15 @@ export const FIELDS = {
|
||||
customFolders: { config: ['storage.customFolders'], advanced: true, output: ({ overrides }) => overrides },
|
||||
externalLibraries: { config: ['storage.externalLibraries'], advanced: true, output: ({ libraries }) => libraries },
|
||||
transcoding: { config: ['hwaccel.transcoding'], output: ({ fragment }) => fragment('transcoding') },
|
||||
ml: { config: ['hwaccel.ml'], output: ({ ml, fragment }) => [[...ml, 'image'], ...fragment('ml')] },
|
||||
ml: {
|
||||
config: ['hwaccel.ml'],
|
||||
output: ({ ml, fragment, bundledMl }) => (bundledMl ? [[...ml, 'image'], ...fragment('ml')] : []),
|
||||
},
|
||||
mlExternal: {
|
||||
config: ['machineLearning.external'],
|
||||
advanced: true,
|
||||
output: ({ ml, bundledMl }) => (bundledMl ? [ml] : []),
|
||||
},
|
||||
databaseExternal: {
|
||||
config: ['database.external'],
|
||||
advanced: true,
|
||||
@@ -216,6 +225,9 @@ export const DEFAULT_CONFIG: ImmichConfig = {
|
||||
transcoding: 'cpu',
|
||||
ml: 'cpu',
|
||||
},
|
||||
machineLearning: {
|
||||
external: false,
|
||||
},
|
||||
storage: {
|
||||
uploadLocation: './library',
|
||||
customFolders: false,
|
||||
|
||||
@@ -61,6 +61,15 @@ const configs: [string, ImmichConfig][] = [
|
||||
return config;
|
||||
})(),
|
||||
],
|
||||
[
|
||||
'remote machine learning',
|
||||
(() => {
|
||||
const config = structuredClone(DEFAULT_CONFIG);
|
||||
config.machineLearning.external = true;
|
||||
config.hwaccel.ml = 'openvino';
|
||||
return config;
|
||||
})(),
|
||||
],
|
||||
];
|
||||
|
||||
const everything = configs[1][1];
|
||||
|
||||
@@ -499,11 +499,32 @@
|
||||
<CardTitle>Machine Learning</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<Field label="Hardware Acceleration">
|
||||
<div data-field="ml">
|
||||
<Select bind:value={config.hwaccel.ml} options={ML_ACCELS} />
|
||||
</div>
|
||||
</Field>
|
||||
<Stack gap={4}>
|
||||
{#if advanced}
|
||||
<Field label="Remote machine learning">
|
||||
<Switch
|
||||
bind:checked={config.machineLearning.external}
|
||||
data-field="mlExternal"
|
||||
class="flex justify-between gap-4"
|
||||
/>
|
||||
</Field>
|
||||
{/if}
|
||||
{#if advanced && config.machineLearning.external}
|
||||
<Text size="small" color="muted">
|
||||
Point your server at the remote instance in the admin settings. See the
|
||||
<Link href="https://docs.immich.app/guides/remote-machine-learning"
|
||||
>remote machine learning guide</Link
|
||||
>.
|
||||
</Text>
|
||||
{/if}
|
||||
{#if !config.machineLearning.external}
|
||||
<Field label="Hardware Acceleration">
|
||||
<div data-field="ml">
|
||||
<Select bind:value={config.hwaccel.ml} options={ML_ACCELS} />
|
||||
</div>
|
||||
</Field>
|
||||
{/if}
|
||||
</Stack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user