feat: add comfy namespace and update comfy examples
This commit is contained in:
commit
4b3080c9a2
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import * as fal from '@fal-ai/serverless-client';
|
import * as fal from '@fal-ai/serverless-client';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import getWorkflow from './workflow';
|
|
||||||
|
|
||||||
// @snippet:start(client.config)
|
// @snippet:start(client.config)
|
||||||
fal.config({
|
fal.config({
|
||||||
@ -84,23 +83,26 @@ export default function ComfyImageToImagePage() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
try {
|
try {
|
||||||
const result: Result = await fal.subscribe('fal-ai/comfy-server', {
|
const result: Result = await fal.subscribe(
|
||||||
input: getWorkflow({
|
'comfy/fal-ai/image-to-image',
|
||||||
prompt: prompt,
|
{
|
||||||
loadimage_1: imageFile,
|
input: {
|
||||||
}),
|
prompt: prompt,
|
||||||
pollInterval: 3000, // Default is 1000 (every 1s)
|
loadimage_1: imageFile,
|
||||||
logs: true,
|
},
|
||||||
onQueueUpdate(update) {
|
pollInterval: 3000, // Default is 1000 (every 1s)
|
||||||
setElapsedTime(Date.now() - start);
|
logs: true,
|
||||||
if (
|
onQueueUpdate(update) {
|
||||||
update.status === 'IN_PROGRESS' ||
|
setElapsedTime(Date.now() - start);
|
||||||
update.status === 'COMPLETED'
|
if (
|
||||||
) {
|
update.status === 'IN_PROGRESS' ||
|
||||||
setLogs((update.logs || []).map((log) => log.message));
|
update.status === 'COMPLETED'
|
||||||
}
|
) {
|
||||||
},
|
setLogs((update.logs || []).map((log) => log.message));
|
||||||
});
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
setResult(getImageURL(result));
|
setResult(getImageURL(result));
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setError(error);
|
setError(error);
|
||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import * as fal from '@fal-ai/serverless-client';
|
import * as fal from '@fal-ai/serverless-client';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import getWorkflow from './workflow';
|
|
||||||
|
|
||||||
// @snippet:start(client.config)
|
// @snippet:start(client.config)
|
||||||
fal.config({
|
fal.config({
|
||||||
@ -80,22 +79,25 @@ export default function ComfyImageToVideoPage() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
try {
|
try {
|
||||||
const result: Result = await fal.subscribe('fal-ai/comfy-server', {
|
const result: Result = await fal.subscribe(
|
||||||
input: getWorkflow({
|
'comfy/fal-ai/image-to-video',
|
||||||
loadimage_1: imageFile,
|
{
|
||||||
}),
|
input: {
|
||||||
pollInterval: 3000, // Default is 1000 (every 1s)
|
loadimage_1: imageFile,
|
||||||
logs: true,
|
},
|
||||||
onQueueUpdate(update) {
|
pollInterval: 3000, // Default is 1000 (every 1s)
|
||||||
setElapsedTime(Date.now() - start);
|
logs: true,
|
||||||
if (
|
onQueueUpdate(update) {
|
||||||
update.status === 'IN_PROGRESS' ||
|
setElapsedTime(Date.now() - start);
|
||||||
update.status === 'COMPLETED'
|
if (
|
||||||
) {
|
update.status === 'IN_PROGRESS' ||
|
||||||
setLogs((update.logs || []).map((log) => log.message));
|
update.status === 'COMPLETED'
|
||||||
}
|
) {
|
||||||
},
|
setLogs((update.logs || []).map((log) => log.message));
|
||||||
});
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
setResult(getImageURL(result));
|
setResult(getImageURL(result));
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setError(error);
|
setError(error);
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -16,19 +16,19 @@ export default function Index() {
|
|||||||
</p>
|
</p>
|
||||||
<div className="mt-12 grid grid-cols-1 gap-3 md:grid-cols-3 lg:grid-cols-3">
|
<div className="mt-12 grid grid-cols-1 gap-3 md:grid-cols-3 lg:grid-cols-3">
|
||||||
<button
|
<button
|
||||||
onClick={() => router.push('/comfy/text_to_image')}
|
onClick={() => router.push('/comfy/text-to-image')}
|
||||||
className="px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-lg shadow-md transition-transform transform hover:-translate-y-1"
|
className="px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-lg shadow-md transition-transform transform hover:-translate-y-1"
|
||||||
>
|
>
|
||||||
Text to Image
|
Text to Image
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => router.push('/comfy/image_to_image')}
|
onClick={() => router.push('/comfy/image-to-image')}
|
||||||
className="px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-lg shadow-md transition-transform transform hover:-translate-y-1"
|
className="px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-lg shadow-md transition-transform transform hover:-translate-y-1"
|
||||||
>
|
>
|
||||||
Image to Image
|
Image to Image
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => router.push('/comfy/image_to_video')}
|
onClick={() => router.push('/comfy/image-to-video')}
|
||||||
className="px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-lg shadow-md transition-transform transform hover:-translate-y-1"
|
className="px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-lg shadow-md transition-transform transform hover:-translate-y-1"
|
||||||
>
|
>
|
||||||
Image to Video
|
Image to Video
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import * as fal from '@fal-ai/serverless-client';
|
import * as fal from '@fal-ai/serverless-client';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import getWorkflow from './workflow';
|
|
||||||
|
|
||||||
// @snippet:start(client.config)
|
// @snippet:start(client.config)
|
||||||
fal.config({
|
fal.config({
|
||||||
@ -83,10 +82,10 @@ export default function ComfyTextToImagePage() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
try {
|
try {
|
||||||
const result: Result = await fal.subscribe('fal-ai/comfy-server', {
|
const result: Result = await fal.subscribe('comfy/fal-ai/text-to-image', {
|
||||||
input: getWorkflow({
|
input: {
|
||||||
prompt: prompt,
|
prompt: prompt,
|
||||||
}),
|
},
|
||||||
pollInterval: 3000, // Default is 1000 (every 1s)
|
pollInterval: 3000, // Default is 1000 (every 1s)
|
||||||
logs: true,
|
logs: true,
|
||||||
onQueueUpdate(update) {
|
onQueueUpdate(update) {
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -103,7 +103,7 @@ export const storageImpl: StorageSupport = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
transformInput: async (input: any) => {
|
transformInput: async (input: any): Promise<any> => {
|
||||||
if (Array.isArray(input)) {
|
if (Array.isArray(input)) {
|
||||||
return Promise.all(input.map((item) => storageImpl.transformInput(item)));
|
return Promise.all(input.map((item) => storageImpl.transformInput(item)));
|
||||||
} else if (
|
} else if (
|
||||||
@ -119,9 +119,12 @@ export const storageImpl: StorageSupport = {
|
|||||||
const url = await storageImpl.upload(blob as Blob);
|
const url = await storageImpl.upload(blob as Blob);
|
||||||
return url;
|
return url;
|
||||||
} else if (isPlainObject(input)) {
|
} else if (isPlainObject(input)) {
|
||||||
const promises = Object.entries(input).map(async ([key, value]) => {
|
const inputObject = input as Record<string, any>;
|
||||||
return [key, await storageImpl.transformInput(value)];
|
const promises = Object.entries(inputObject).map(
|
||||||
});
|
async ([key, value]): Promise<KeyValuePair> => {
|
||||||
|
return [key, await storageImpl.transformInput(value)];
|
||||||
|
}
|
||||||
|
);
|
||||||
const results = await Promise.all(promises);
|
const results = await Promise.all(promises);
|
||||||
return Object.fromEntries(results);
|
return Object.fromEntries(results);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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];
|
type AppNamespace = (typeof APP_NAMESPACES)[number];
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user