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() {