From fc461ed48b71e3b852c5af97705cb3c42fcbd742 Mon Sep 17 00:00:00 2001 From: badayvedat Date: Thu, 6 Jun 2024 18:34:57 +0300 Subject: [PATCH 1/3] feat: add comfy namespace and update comfy examples --- .../app/comfy/image_to_image/page.tsx | 38 ++++--- .../app/comfy/image_to_image/workflow.tsx | 102 ----------------- .../app/comfy/image_to_video/page.tsx | 36 +++--- .../app/comfy/image_to_video/workflow.tsx | 91 ---------------- .../app/comfy/text_to_image/page.tsx | 7 +- .../app/comfy/text_to_image/workflow.tsx | 103 ------------------ libs/client/src/utils.ts | 2 +- 7 files changed, 43 insertions(+), 336 deletions(-) delete mode 100644 apps/demo-nextjs-app-router/app/comfy/image_to_image/workflow.tsx delete mode 100644 apps/demo-nextjs-app-router/app/comfy/image_to_video/workflow.tsx delete mode 100644 apps/demo-nextjs-app-router/app/comfy/text_to_image/workflow.tsx diff --git a/apps/demo-nextjs-app-router/app/comfy/image_to_image/page.tsx b/apps/demo-nextjs-app-router/app/comfy/image_to_image/page.tsx index 0b5059f..1f349f3 100644 --- a/apps/demo-nextjs-app-router/app/comfy/image_to_image/page.tsx +++ b/apps/demo-nextjs-app-router/app/comfy/image_to_image/page.tsx @@ -2,7 +2,6 @@ import * as fal from '@fal-ai/serverless-client'; import { useMemo, useState } from 'react'; -import getWorkflow from './workflow'; // @snippet:start(client.config) fal.config({ @@ -84,23 +83,26 @@ export default function ComfyImageToImagePage() { setLoading(true); const start = Date.now(); try { - const result: Result = await fal.subscribe('fal-ai/comfy-server', { - input: getWorkflow({ - prompt: prompt, - loadimage_1: imageFile, - }), - pollInterval: 3000, // Default is 1000 (every 1s) - logs: true, - onQueueUpdate(update) { - setElapsedTime(Date.now() - start); - if ( - update.status === 'IN_PROGRESS' || - update.status === 'COMPLETED' - ) { - setLogs((update.logs || []).map((log) => log.message)); - } - }, - }); + const result: Result = await fal.subscribe( + 'comfy/fal-ai/image-to-image', + { + input: { + prompt: prompt, + loadimage_1: imageFile, + }, + pollInterval: 3000, // Default is 1000 (every 1s) + logs: true, + onQueueUpdate(update) { + setElapsedTime(Date.now() - start); + if ( + update.status === 'IN_PROGRESS' || + update.status === 'COMPLETED' + ) { + setLogs((update.logs || []).map((log) => log.message)); + } + }, + } + ); setResult(getImageURL(result)); } catch (error: any) { setError(error); diff --git a/apps/demo-nextjs-app-router/app/comfy/image_to_image/workflow.tsx b/apps/demo-nextjs-app-router/app/comfy/image_to_image/workflow.tsx deleted file mode 100644 index 0e43d41..0000000 --- a/apps/demo-nextjs-app-router/app/comfy/image_to_image/workflow.tsx +++ /dev/null @@ -1,102 +0,0 @@ -// This workflow is generated with ComfyUI-fal -const WORKFLOW = { - prompt: { - '3': { - inputs: { - seed: 280823642470253, - steps: 20, - cfg: 8, - sampler_name: 'dpmpp_2m', - scheduler: 'normal', - denoise: 0.8700000000000001, - model: ['14', 0], - positive: ['6', 0], - negative: ['7', 0], - latent_image: ['12', 0], - }, - class_type: 'KSampler', - }, - '6': { - inputs: { - text: ['15', 0], - clip: ['14', 1], - }, - class_type: 'CLIPTextEncode', - }, - '7': { - inputs: { - text: 'watermark, text\n', - clip: ['14', 1], - }, - class_type: 'CLIPTextEncode', - }, - '8': { - inputs: { - samples: ['3', 0], - vae: ['14', 2], - }, - class_type: 'VAEDecode', - }, - '9': { - inputs: { - filename_prefix: 'ComfyUI', - images: ['8', 0], - }, - class_type: 'SaveImage', - }, - '10': { - inputs: { - image: 'example.png', - upload: 'image', - }, - class_type: 'LoadImage', - }, - '12': { - inputs: { - pixels: ['10', 0], - vae: ['14', 2], - }, - class_type: 'VAEEncode', - }, - '14': { - inputs: { - ckpt_name: 'v1-5-pruned-emaonly.ckpt', - }, - class_type: 'CheckpointLoaderSimple', - }, - '15': { - inputs: { - name: 'prompt', - value: - 'photograph of victorian woman with wings, sky clouds, meadow grass\n', - }, - class_type: 'StringInput_fal', - }, - }, - extra_data: {}, - fal_inputs_dev_info: { - loadimage_1: { - key: ['10', 'inputs', 'image'], - class_type: 'LoadImage', - }, - prompt: { - key: ['15', 'inputs', 'value'], - class_type: 'StringInput_fal', - }, - }, - fal_inputs: { - loadimage_1: 'example_url', - prompt: - 'photograph of victorian woman with wings, sky clouds, meadow grass\n', - }, -}; - -export default function getWorkflow(object: any) { - const newWorkflow = JSON.parse(JSON.stringify(WORKFLOW)); - newWorkflow.fal_inputs = { - ...newWorkflow.fal_inputs, - ...object, - }; - - return newWorkflow; -} diff --git a/apps/demo-nextjs-app-router/app/comfy/image_to_video/page.tsx b/apps/demo-nextjs-app-router/app/comfy/image_to_video/page.tsx index e969294..037fc3c 100644 --- a/apps/demo-nextjs-app-router/app/comfy/image_to_video/page.tsx +++ b/apps/demo-nextjs-app-router/app/comfy/image_to_video/page.tsx @@ -2,7 +2,6 @@ import * as fal from '@fal-ai/serverless-client'; import { useMemo, useState } from 'react'; -import getWorkflow from './workflow'; // @snippet:start(client.config) fal.config({ @@ -80,22 +79,25 @@ export default function ComfyImageToVideoPage() { setLoading(true); const start = Date.now(); try { - const result: Result = await fal.subscribe('fal-ai/comfy-server', { - input: getWorkflow({ - loadimage_1: imageFile, - }), - pollInterval: 3000, // Default is 1000 (every 1s) - logs: true, - onQueueUpdate(update) { - setElapsedTime(Date.now() - start); - if ( - update.status === 'IN_PROGRESS' || - update.status === 'COMPLETED' - ) { - setLogs((update.logs || []).map((log) => log.message)); - } - }, - }); + const result: Result = await fal.subscribe( + 'comfy/fal-ai/image-to-video', + { + input: { + loadimage_1: imageFile, + }, + pollInterval: 3000, // Default is 1000 (every 1s) + logs: true, + onQueueUpdate(update) { + setElapsedTime(Date.now() - start); + if ( + update.status === 'IN_PROGRESS' || + update.status === 'COMPLETED' + ) { + setLogs((update.logs || []).map((log) => log.message)); + } + }, + } + ); setResult(getImageURL(result)); } catch (error: any) { setError(error); diff --git a/apps/demo-nextjs-app-router/app/comfy/image_to_video/workflow.tsx b/apps/demo-nextjs-app-router/app/comfy/image_to_video/workflow.tsx deleted file mode 100644 index fc3baba..0000000 --- a/apps/demo-nextjs-app-router/app/comfy/image_to_video/workflow.tsx +++ /dev/null @@ -1,91 +0,0 @@ -const WORKFLOW = { - prompt: { - '3': { - inputs: { - seed: 351912937281939, - steps: 20, - cfg: 2.5, - sampler_name: 'euler', - scheduler: 'karras', - denoise: 1, - model: ['14', 0], - positive: ['12', 0], - negative: ['12', 1], - latent_image: ['12', 2], - }, - class_type: 'KSampler', - }, - '8': { - inputs: { - samples: ['3', 0], - vae: ['15', 2], - }, - class_type: 'VAEDecode', - }, - '10': { - inputs: { - filename_prefix: 'ComfyUI', - fps: 10, - lossless: false, - quality: 85, - method: 'default', - images: ['8', 0], - }, - class_type: 'SaveAnimatedWEBP', - }, - '12': { - inputs: { - width: 1024, - height: 576, - video_frames: 14, - motion_bucket_id: 127, - fps: 6, - augmentation_level: 0, - clip_vision: ['15', 1], - init_image: ['23', 0], - vae: ['15', 2], - }, - class_type: 'SVD_img2vid_Conditioning', - }, - '14': { - inputs: { - min_cfg: 1, - model: ['15', 0], - }, - class_type: 'VideoLinearCFGGuidance', - }, - '15': { - inputs: { - ckpt_name: 'svd.safetensors', - }, - class_type: 'ImageOnlyCheckpointLoader', - }, - '23': { - inputs: { - image: '18.png', - upload: 'image', - }, - class_type: 'LoadImage', - }, - }, - extra_data: {}, - fal_inputs_dev_info: { - loadimage_1: { - key: ['23', 'inputs', 'image'], - class_type: 'LoadImage', - }, - }, - fal_inputs: { - loadimage_1: 'example_url', - }, -}; - -export default function getWorkflow(object: any) { - const newWorkflow = JSON.parse(JSON.stringify(WORKFLOW)); - newWorkflow.fal_inputs = { - ...newWorkflow.fal_inputs, - ...object, - }; - - return newWorkflow; -} diff --git a/apps/demo-nextjs-app-router/app/comfy/text_to_image/page.tsx b/apps/demo-nextjs-app-router/app/comfy/text_to_image/page.tsx index eb7810a..45ccd30 100644 --- a/apps/demo-nextjs-app-router/app/comfy/text_to_image/page.tsx +++ b/apps/demo-nextjs-app-router/app/comfy/text_to_image/page.tsx @@ -2,7 +2,6 @@ import * as fal from '@fal-ai/serverless-client'; import { useMemo, useState } from 'react'; -import getWorkflow from './workflow'; // @snippet:start(client.config) fal.config({ @@ -83,10 +82,10 @@ export default function ComfyTextToImagePage() { setLoading(true); const start = Date.now(); try { - const result: Result = await fal.subscribe('fal-ai/comfy-server', { - input: getWorkflow({ + const result: Result = await fal.subscribe('comfy/fal-ai/text-to-image', { + input: { prompt: prompt, - }), + }, pollInterval: 3000, // Default is 1000 (every 1s) logs: true, onQueueUpdate(update) { diff --git a/apps/demo-nextjs-app-router/app/comfy/text_to_image/workflow.tsx b/apps/demo-nextjs-app-router/app/comfy/text_to_image/workflow.tsx deleted file mode 100644 index 30ea0d4..0000000 --- a/apps/demo-nextjs-app-router/app/comfy/text_to_image/workflow.tsx +++ /dev/null @@ -1,103 +0,0 @@ -// This workflow is generated with ComfyUI-fal -const WORKFLOW = { - prompt: { - '3': { - inputs: { - seed: 704126934460886, - steps: 20, - cfg: 8, - sampler_name: 'euler', - scheduler: 'normal', - denoise: 1, - model: ['4', 0], - positive: ['6', 0], - negative: ['7', 0], - latent_image: ['5', 0], - }, - class_type: 'KSampler', - }, - '4': { - inputs: { - ckpt_name: 'sd_xl_1.0.safetensors', - }, - class_type: 'CheckpointLoaderSimple', - }, - '5': { - inputs: { - width: 1024, - height: 1024, - batch_size: 1, - }, - class_type: 'EmptyLatentImage', - }, - '6': { - inputs: { - text: ['10', 0], - clip: ['4', 1], - }, - class_type: 'CLIPTextEncode', - }, - '7': { - inputs: { - text: ['11', 0], - clip: ['4', 1], - }, - class_type: 'CLIPTextEncode', - }, - '8': { - inputs: { - samples: ['3', 0], - vae: ['4', 2], - }, - class_type: 'VAEDecode', - }, - '9': { - inputs: { - filename_prefix: 'ComfyUI', - images: ['8', 0], - }, - class_type: 'SaveImage', - }, - '10': { - inputs: { - name: 'prompt', - value: - 'beautiful scenery nature glass bottle landscape, , purple galaxy bottle,', - }, - class_type: 'StringInput_fal', - }, - '11': { - inputs: { - name: 'negative_prompt', - value: 'text, watermark', - }, - class_type: 'StringInput_fal', - }, - }, - extra_data: {}, - fal_inputs_dev_info: { - prompt: { - key: ['10', 'inputs', 'value'], - class_type: 'StringInput_fal', - }, - negative_prompt: { - key: ['11', 'inputs', 'value'], - class_type: 'StringInput_fal', - }, - }, - fal_inputs: { - prompt: - 'beautiful scenery nature glass bottle landscape, , purple galaxy bottle,', - negative_prompt: 'text, watermark', - }, -}; - -export default function getWorkflow(object: any) { - const newWorkflow = JSON.parse(JSON.stringify(WORKFLOW)); - newWorkflow.fal_inputs = { - ...newWorkflow.fal_inputs, - ...object, - }; - - return newWorkflow; -} diff --git a/libs/client/src/utils.ts b/libs/client/src/utils.ts index 8f6fd22..dd9117d 100644 --- a/libs/client/src/utils.ts +++ b/libs/client/src/utils.ts @@ -21,7 +21,7 @@ export function ensureAppIdFormat(id: string): string { ); } -const APP_NAMESPACES = ['workflows'] as const; +const APP_NAMESPACES = ['workflows', 'comfy'] as const; type AppNamespace = (typeof APP_NAMESPACES)[number]; From 21bb392a19fa977fe0594aafb4f9e3d688bfc3db Mon Sep 17 00:00:00 2001 From: badayvedat Date: Thu, 6 Jun 2024 18:54:09 +0300 Subject: [PATCH 2/3] fix: type updates --- libs/client/src/storage.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libs/client/src/storage.ts b/libs/client/src/storage.ts index 3871974..8c3e213 100644 --- a/libs/client/src/storage.ts +++ b/libs/client/src/storage.ts @@ -103,7 +103,7 @@ export const storageImpl: StorageSupport = { }, // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformInput: async (input: any) => { + transformInput: async (input: any): Promise => { if (Array.isArray(input)) { return Promise.all(input.map((item) => storageImpl.transformInput(item))); } else if ( @@ -119,9 +119,12 @@ export const storageImpl: StorageSupport = { const url = await storageImpl.upload(blob as Blob); return url; } else if (isPlainObject(input)) { - const promises = Object.entries(input).map(async ([key, value]) => { - return [key, await storageImpl.transformInput(value)]; - }); + const inputObject = input as Record; + const promises = Object.entries(inputObject).map( + async ([key, value]): Promise => { + return [key, await storageImpl.transformInput(value)]; + } + ); const results = await Promise.all(promises); return Object.fromEntries(results); } From 7a494ff6973c4a2e993f4a9a3da837bfa390d700 Mon Sep 17 00:00:00 2001 From: badayvedat Date: Tue, 11 Jun 2024 19:41:46 +0300 Subject: [PATCH 3/3] refactor: use dash instead of underscore in paths --- .../app/comfy/{image_to_image => image-to-image}/page.tsx | 0 .../app/comfy/{image_to_video => image-to-video}/page.tsx | 0 apps/demo-nextjs-app-router/app/comfy/page.tsx | 6 +++--- .../app/comfy/{text_to_image => text-to-image}/page.tsx | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename apps/demo-nextjs-app-router/app/comfy/{image_to_image => image-to-image}/page.tsx (100%) rename apps/demo-nextjs-app-router/app/comfy/{image_to_video => image-to-video}/page.tsx (100%) rename apps/demo-nextjs-app-router/app/comfy/{text_to_image => text-to-image}/page.tsx (100%) diff --git a/apps/demo-nextjs-app-router/app/comfy/image_to_image/page.tsx b/apps/demo-nextjs-app-router/app/comfy/image-to-image/page.tsx similarity index 100% rename from apps/demo-nextjs-app-router/app/comfy/image_to_image/page.tsx rename to apps/demo-nextjs-app-router/app/comfy/image-to-image/page.tsx diff --git a/apps/demo-nextjs-app-router/app/comfy/image_to_video/page.tsx b/apps/demo-nextjs-app-router/app/comfy/image-to-video/page.tsx similarity index 100% rename from apps/demo-nextjs-app-router/app/comfy/image_to_video/page.tsx rename to apps/demo-nextjs-app-router/app/comfy/image-to-video/page.tsx diff --git a/apps/demo-nextjs-app-router/app/comfy/page.tsx b/apps/demo-nextjs-app-router/app/comfy/page.tsx index 70e6d93..9acca73 100644 --- a/apps/demo-nextjs-app-router/app/comfy/page.tsx +++ b/apps/demo-nextjs-app-router/app/comfy/page.tsx @@ -16,19 +16,19 @@ export default function Index() {