Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot] 87b3c3b258 chore(deps): update machine-learning 2026-05-29 14:06:29 +00:00
4 changed files with 526 additions and 545 deletions
+4 -4
View File
@@ -1,8 +1,8 @@
ARG DEVICE=cpu
FROM python:3.11-bookworm@sha256:970c99f886b839fc8829289040c1845dadaf2cae46b37acc7710333158ec29b4 AS builder-cpu
FROM python:3.11-bookworm@sha256:121d86b6d08752968a7dddbc708849e5f3a839bbff47f32212b46d2a1d842bab AS builder-cpu
FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec5fb2f8f6403244621664 AS builder-openvino
FROM python:3.13-slim-trixie@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3 AS builder-openvino
FROM builder-cpu AS builder-cuda
@@ -39,12 +39,12 @@ RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --extra ${DEVICE} --no-dev --no-editable --no-install-project --compile-bytecode --no-progress --active --link-mode copy
FROM python:3.11-slim-bookworm@sha256:9c6f90801e6b68e772b7c0ca74260cbf7af9f320acec894e26fccdaccfbe3b47 AS prod-cpu
FROM python:3.11-slim-bookworm@sha256:8dca233de9f3d9bb410665f00a4da6dd06f331083137e0e98ccf227236fcc438 AS prod-cpu
ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2 \
MACHINE_LEARNING_MODEL_ARENA=false
FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec5fb2f8f6403244621664 AS prod-openvino
FROM python:3.13-slim-trixie@sha256:b04b5d7233d2ad9c379e22ea8927cd1378cd15c60d4ef876c065b25ea8fb3bf3 AS prod-openvino
RUN apt-get update && \
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
+495 -447
View File
File diff suppressed because it is too large Load Diff
@@ -7,7 +7,7 @@
import { Route } from '$lib/route';
import { getWorkflowActions, handleUpdateWorkflow } from '$lib/services/workflow.service';
import { getTriggerDescription, getTriggerName } from '$lib/utils/workflow';
import type { WorkflowResponseDto, WorkflowStepDto, WorkflowUpdateDto } from '@immich/sdk';
import type { WorkflowResponseDto, WorkflowUpdateDto } from '@immich/sdk';
import {
ActionBar,
AppShell,
@@ -48,8 +48,6 @@
import WorkflowJsonEditor from './WorkflowJsonEditor.svelte';
import WorkflowStepCard from './WorkflowStepCard.svelte';
import WorkflowSummary from './WorkflowSummary.svelte';
import { flip } from 'svelte/animate';
import { generateId } from '$lib/utils/generate-id';
type WorkflowJsonContent = Required<
Pick<WorkflowUpdateDto, 'description' | 'enabled' | 'name' | 'steps' | 'trigger'>
@@ -64,8 +62,7 @@
let { data }: Props = $props();
let { id, enabled, name, description, trigger } = $derived(data.workflow);
let steps = $state(data.workflow.steps.map((step) => ({ ...step, id: generateId() })));
let tempSteps = $state<(WorkflowStepDto & { id: string })[]>();
let steps = $state(data.workflow.steps);
let savedWorkflow = $state(cloneDeep(data.workflow));
let allowNavigation = $state(false);
let isShowingNavigationDialog = $state(false);
@@ -80,23 +77,20 @@
name !== savedWorkflow.name ||
description !== savedWorkflow.description ||
!isEqual(trigger, savedWorkflow.trigger) ||
!isEqual(
steps.map(({ id: _, ...step }) => step),
savedWorkflow.steps,
),
!isEqual(steps, savedWorkflow.steps),
);
const handleAddStep = async () => {
const step = await modalManager.show(WorkflowAddStepModal, { trigger });
if (step) {
steps.push({ ...step, id: generateId() });
steps.push(step);
}
};
const handleInsertStep = async (index: number) => {
const step = await modalManager.show(WorkflowAddStepModal, { trigger });
if (step) {
steps = [...steps.slice(0, index), { ...step, id: generateId() }, ...steps.slice(index)];
steps = [...steps.slice(0, index), step, ...steps.slice(index)];
}
};
@@ -108,47 +102,23 @@
const result = await modalManager.show(WorkflowEditStepModal, { trigger, step: cloneDeep(step) });
if (result) {
steps[index] = { ...result, id: generateId() };
steps[index] = result;
}
};
const handleDragEnd = (index: number, event: DragEvent) => {
const handleDrop = (index: number, event: DragEvent) => {
if (!event.dataTransfer) {
return;
}
tempSteps = undefined;
const from = Number(event.dataTransfer.getData('text/plain'));
if (from === index) {
return;
}
const next = [...steps];
const [moved] = next.splice(from, 1);
next.splice(index, 0, moved);
steps = next;
};
const handleDragOver = (index: number, event: DragEvent) => {
if (!event.dataTransfer) {
return;
}
const from = Number(event.dataTransfer.getData('text/plain'));
if (from === index) {
return;
}
const next = [...steps];
next.splice(index, 0, { ...next[from], id: generateId() });
steps = next;
event.dataTransfer.setData('text/plain', String(index));
};
$effect(() => console.log(tempSteps));
const handleDeleteStep = async (index: number) => {
const confirmed = await modalManager.showDialog({ title: $t('step_delete'), prompt: $t('step_delete_confirm') });
if (confirmed) {
@@ -161,7 +131,7 @@
name = content.name;
description = content.description;
trigger = content.trigger;
steps = cloneDeep(content.steps).map((step) => ({ ...step, id: generateId() }));
steps = cloneDeep(content.steps);
};
const onClose = () => goto(Route.workflows());
@@ -374,20 +344,15 @@
</CardHeader>
</Card>
{#each tempSteps ?? steps as step, index (step.id)}
<div class="w-full" animate:flip={{ duration: 120 }}>
<WorkflowStepCard
{step}
{index}
ghost={!!tempSteps}
onEdit={handleEditStep}
onDelete={handleDeleteStep}
onInsertBefore={handleInsertStep}
onDrop={() => {}}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
/>
</div>
{#each steps as step, index (step.method + index)}
<WorkflowStepCard
{step}
{index}
onEdit={handleEditStep}
onDelete={handleDeleteStep}
onInsertBefore={handleInsertStep}
onDrop={handleDrop}
/>
{/each}
<Button
@@ -1,8 +1,6 @@
<script lang="ts">
import { authManager } from '$lib/managers/auth-manager.svelte';
import { pluginManager } from '$lib/managers/plugin-manager.svelte';
import type { JSONSchemaProperty } from '$lib/types';
import { getAlbumInfo, type WorkflowStepDto } from '@immich/sdk';
import type { WorkflowStepDto } from '@immich/sdk';
import { Badge, Card, CardBody, CardDescription, CardHeader, CardTitle, Icon, IconButton } from '@immich/ui';
import {
mdiAutoFix,
@@ -19,43 +17,23 @@
type Props = {
step: WorkflowStepDto;
index: number;
ghost: boolean;
onEdit: (index: number) => void;
onDelete: (index: number) => void;
onInsertBefore: (index: number) => void;
onDrop: (index: number, event: DragEvent) => void;
onDragOver: (index: number, event: DragEvent) => void;
onDragEnd: (index: number, event: DragEvent) => void;
};
let { step, index, ghost, onEdit, onDelete, onInsertBefore, onDrop, onDragOver, onDragEnd }: Props = $props();
let { step, index, onEdit, onDelete, onInsertBefore, onDrop }: Props = $props();
const method = $derived(pluginManager.getMethod(step.method));
const isFilter = $derived(method?.uiHints?.includes('Filter') ?? false);
const schema = $derived(method?.schema as JSONSchemaProperty | undefined);
const configEntries = $derived(
Object.entries(step.config ?? {}).filter(([, value]) => value !== null && value !== undefined && value !== ''),
);
const getUiHint = (key: string) => schema?.properties?.[key]?.uiHint;
const toIds = (value: unknown): string[] => (Array.isArray(value) ? value.map(String) : [String(value)]);
let dragImage = $state<Element>();
let isDropTarget = $state(false);
let hoverDrag = $state(false);
// eslint-disable-next-line svelte/prefer-svelte-reactivity
const albumNameCache = new Map<string, Promise<string>>();
const getAlbumName = (id: string): Promise<string> => {
let albumName = albumNameCache.get(id);
if (!albumName) {
albumName = getAlbumInfo({ ...authManager.params, id })
.then((album) => album.albumName)
.catch(() => id);
albumNameCache.set(id, albumName);
}
return albumName;
};
const truncate = (input: string, max = 24) => (input.length > max ? input.slice(0, max - 1) + '…' : input);
const formatConfigValue = (value: unknown): string => {
@@ -113,20 +91,23 @@
}
event.preventDefault();
const from = Number(event.dataTransfer.getData('text/plain'));
if (from === index) {
return;
}
onDrop(index, event);
};
const handleDragOver = (event: DragEvent) => {
event.preventDefault();
isDropTarget = true;
onDragOver(index, event);
};
const handleDragEnd = (event: DragEvent) => {
const handleDragEnd = () => {
dragImage?.remove();
dragImage = undefined;
isDropTarget = false;
onDragEnd(index, event);
};
</script>
@@ -223,28 +204,15 @@
{#if configEntries.length > 0}
<CardBody class="py-3">
<div class="flex flex-wrap items-center gap-1.5">
{#snippet badge(key: string, content: string)}
{#each configEntries as [key, value] (key)}
<Badge
color={isFilter ? 'info' : 'warning'}
shape="round"
size="small"
class="border font-mono {isFilter ? 'border-primary-200' : 'border-warning-200'}"
>
<span class="opacity-60">{key}</span>{content}
<span class="opacity-60">{key}</span>{formatConfigValue(value)}
</Badge>
{/snippet}
{#each configEntries as [key, value] (key)}
{#if getUiHint(key) === 'AlbumId'}
{#each toIds(value) as albumId (albumId)}
{#await getAlbumName(albumId)}
{@render badge($t('album'), '…')}
{:then albumName}
{@render badge($t('album'), `"${truncate(albumName)}"`)}
{/await}
{/each}
{:else}
{@render badge(key, formatConfigValue(value))}
{/if}
{/each}
</div>
</CardBody>