diff --git a/package-lock.json b/package-lock.json index 3bca322a..7d139a5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,12 @@ "version": "0.0.8", "license": "GPL-3.0-only", "dependencies": { - "@aitube/broadway": "0.2.2", - "@aitube/clap": "0.2.2", - "@aitube/clapper-services": "0.2.2-1", - "@aitube/client": "0.2.2", - "@aitube/engine": "0.2.2", - "@aitube/timeline": "0.2.2-1", + "@aitube/broadway": "0.2.3", + "@aitube/clap": "0.2.3", + "@aitube/clapper-services": "0.2.3-1", + "@aitube/client": "0.2.3", + "@aitube/engine": "0.2.3", + "@aitube/timeline": "0.2.3", "@fal-ai/serverless-client": "^0.13.0", "@ffmpeg/ffmpeg": "^0.12.10", "@ffmpeg/util": "^0.12.1", @@ -55,6 +55,7 @@ "@react-three/fiber": "^8.16.6", "@react-three/uikit": "^0.3.4", "@react-three/uikit-lucide": "^0.3.4", + "@saintno/comfyui-sdk": "^0.1.11", "@tailwindcss/container-queries": "^0.1.1", "@types/dom-speech-recognition": "^0.0.4", "@types/pngjs": "^6.0.5", @@ -165,23 +166,23 @@ } }, "node_modules/@aitube/broadway": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@aitube/broadway/-/broadway-0.2.2.tgz", - "integrity": "sha512-Thf+uiTJTVEleWVqgHHuGpSSes+VXRHU2wZA00qSvatE8p2U7tEZTdov56xBx1AmvsQ0AkfPwiDaX8BhSpouDQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aitube/broadway/-/broadway-0.2.3.tgz", + "integrity": "sha512-zuJ8zsVbTury3Phw7flMit8TnlnlLaSympkuxdR1Aea5YeZOOZmJh5+S/Y1f+xkcj9wox2ej8J8XGfIJWbd3RA==", "dependencies": { "@datagica/parse-entities": "^0.3.0", "@datagica/parse-names": "^0.0.8", "indexeddb-fs": "^2.1.5" }, "peerDependencies": { - "@aitube/clap": "0.2.2", - "@aitube/colors": "0.2.2" + "@aitube/clap": "0.2.3", + "@aitube/colors": "0.2.3" } }, "node_modules/@aitube/clap": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@aitube/clap/-/clap-0.2.2.tgz", - "integrity": "sha512-IMsuSCLOq4OIChd2B9otJuk517vszzztamSgycmLaY85zt4xPT1D3Jsy4OwZbfWOT5Fcf5S837yON5YbDO3wMQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aitube/clap/-/clap-0.2.3.tgz", + "integrity": "sha512-qT0wI6R5BrkIWDr1Dog/GPMYjKnwy5glwO3MibPcM0BWJVQlbj0cy0qbwH+wZPC1ZmRZnaJPlhCEj5e3/U6UsA==", "dependencies": { "pure-uuid": "^1.8.1" }, @@ -190,12 +191,12 @@ } }, "node_modules/@aitube/clapper-services": { - "version": "0.2.2-1", - "resolved": "https://registry.npmjs.org/@aitube/clapper-services/-/clapper-services-0.2.2-1.tgz", - "integrity": "sha512-t7Mof/A+Y/nYRE8OXqY+3jgal6SMNVK9aS+Mj6D0QrwSQJvg8I12D2c4vshEgPbw5U2n0c2TSYv/MOx0ptvOsA==", + "version": "0.2.3-1", + "resolved": "https://registry.npmjs.org/@aitube/clapper-services/-/clapper-services-0.2.3-1.tgz", + "integrity": "sha512-R6n9Wlte7E7bRIKh3Jf8xSZkvly2BgQ4p12OJY1rBNzgvDaUfHyFcCdCwKcBXzcDlWzGW5fjQIA7+OPeoXpC0Q==", "peerDependencies": { - "@aitube/clap": "0.2.2", - "@aitube/timeline": "0.2.2-1", + "@aitube/clap": "0.2.3", + "@aitube/timeline": "0.2.3", "@monaco-editor/react": "4.6.0", "monaco-editor": "0.50.0", "react": "*", @@ -204,43 +205,43 @@ } }, "node_modules/@aitube/client": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@aitube/client/-/client-0.2.2.tgz", - "integrity": "sha512-3C86wRnlzJVRR7s4+C8HURu7tZCTWo7zGKdS9qHp0dM/8igPKT6r0FahuoSZQDPmY87S9vkP3ec+Sb0sut8rXQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aitube/client/-/client-0.2.3.tgz", + "integrity": "sha512-REgFjqvK2I+Qe4tbabHwtU5uNxQwOHMXY2XQAIN34Ff7wrd4Y4vaWB3EZ37l/yKgt7Bd3kldp2ddDt09osedMQ==", "dependencies": { "query-string": "^9.0.0" }, "peerDependencies": { - "@aitube/clap": "0.2.2" + "@aitube/clap": "0.2.3" } }, "node_modules/@aitube/colors": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@aitube/colors/-/colors-0.2.2.tgz", - "integrity": "sha512-LTIwh6NVtN8ocvhpmGyvXZe8vtInW9C2Y4Dc4afI2zHv4e03bmffoRQsWcUtrSbEJrSAT0Slk38xEI/hz2s03w==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aitube/colors/-/colors-0.2.3.tgz", + "integrity": "sha512-bqie0eFUG/UA62lhJIibCVPdBNyudqE9YF6HoWMY2JpFtjV3EHJlla3NuXtTgfTgs7XGcmtTyqoXtmPsRSHZtw==", "peer": true, "peerDependencies": { - "@aitube/clap": "0.2.2" + "@aitube/clap": "0.2.3" } }, "node_modules/@aitube/engine": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@aitube/engine/-/engine-0.2.2.tgz", - "integrity": "sha512-y4q5qCvtsiEh/6lKgd8aBXL1YIjIRoxrVoGU5HUHyXYRVHL8/uNGETEIJRlRG1V849J+IyHSqoUhAN32b5Dyeg==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aitube/engine/-/engine-0.2.3.tgz", + "integrity": "sha512-y6KktN1BU5L/WFC5rrNwx0CHdQYM9xPJX1fTPZVDYfZC/6NJhn1Rm1SAnV1SkkP+3NCDQBabKvUivjmZY0CFDw==", "peerDependencies": { - "@aitube/clap": "0.2.2" + "@aitube/clap": "0.2.3" } }, "node_modules/@aitube/timeline": { - "version": "0.2.2-1", - "resolved": "https://registry.npmjs.org/@aitube/timeline/-/timeline-0.2.2-1.tgz", - "integrity": "sha512-anFq6qi+uhES6UYrUYkLXYk15xZ5O8Sa/L09Xlr7eM2ae+KV1e/1x4yoJ+Xd5uLLsH+tWqQv/GgmMVguMUj/cA==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aitube/timeline/-/timeline-0.2.3.tgz", + "integrity": "sha512-zr9G5t/koz/vwXxKHs2qFot5gSau72J+luTfBK0o1tgf/TK/+yP6ACO3QbO8kAmkqa6wtWuBHdVXkVceB8z7Rw==", "dependencies": { "date-fns": "^3.6.0", "react-virtualized-auto-sizer": "^1.0.24" }, "peerDependencies": { - "@aitube/clap": "0.2.2", + "@aitube/clap": "0.2.3", "@radix-ui/react-slider": "^1.1.2", "@react-spring/three": "^9.7.3", "@react-spring/types": "^9.7.3", @@ -6141,6 +6142,32 @@ "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", "dev": true }, + "node_modules/@saintno/comfyui-sdk": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@saintno/comfyui-sdk/-/comfyui-sdk-0.1.11.tgz", + "integrity": "sha512-k8Wui1ejYKOKhEF4WvNiNz3DTCtg+zJSnwKBKP6AHn9xOp+73mNF7F+TXXTTTuq9SCVNQdalEXTkDS6uDnEmxg==", + "dependencies": { + "@types/node": "^22.2.0", + "bun-types": "^1.1.22", + "ws": "^8.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } + }, + "node_modules/@saintno/comfyui-sdk/node_modules/@types/node": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.2.0.tgz", + "integrity": "sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ==", + "dependencies": { + "undici-types": "~6.13.0" + } + }, + "node_modules/@saintno/comfyui-sdk/node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -7195,6 +7222,14 @@ "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==" }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -8376,6 +8411,23 @@ "node": ">=6.14.2" } }, + "node_modules/bun-types": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.1.22.tgz", + "integrity": "sha512-PBgj4pQd+1WZJ8kWCho7D6i1RMS9/6WkiRikIfqYFzFomfyWZET32Wy83xK2zmF9HiKXd2+bjtVahJ6546oraA==", + "dependencies": { + "@types/node": "~20.12.8", + "@types/ws": "~8.5.10" + } + }, + "node_modules/bun-types/node_modules/@types/node": { + "version": "20.12.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz", + "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", diff --git a/package.json b/package.json index 10985651..d74f839f 100644 --- a/package.json +++ b/package.json @@ -36,12 +36,12 @@ "electron:make": "npm run build && electron-forge make" }, "dependencies": { - "@aitube/broadway": "0.2.2", - "@aitube/clap": "0.2.2", - "@aitube/clapper-services": "0.2.2-1", - "@aitube/client": "0.2.2", - "@aitube/engine": "0.2.2", - "@aitube/timeline": "0.2.2-1", + "@aitube/broadway": "0.2.3", + "@aitube/clap": "0.2.3", + "@aitube/clapper-services": "0.2.3-1", + "@aitube/client": "0.2.3", + "@aitube/engine": "0.2.3", + "@aitube/timeline": "0.2.3", "@fal-ai/serverless-client": "^0.13.0", "@ffmpeg/ffmpeg": "^0.12.10", "@ffmpeg/util": "^0.12.1", @@ -82,6 +82,7 @@ "@react-three/fiber": "^8.16.6", "@react-three/uikit": "^0.3.4", "@react-three/uikit-lucide": "^0.3.4", + "@saintno/comfyui-sdk": "^0.1.11", "@tailwindcss/container-queries": "^0.1.1", "@types/dom-speech-recognition": "^0.0.4", "@types/pngjs": "^6.0.5", diff --git a/src/app/api/resolve/providers/comfy/index.ts b/src/app/api/resolve/providers/comfy/index.ts new file mode 100644 index 00000000..52f06438 --- /dev/null +++ b/src/app/api/resolve/providers/comfy/index.ts @@ -0,0 +1,146 @@ +import { ResolveRequest } from '@aitube/clapper-services' +import { + ClapSegmentCategory, + ClapSegmentStatus, + generateSeed, + getClapAssetSourceType, +} from '@aitube/clap' +import { TimelineSegment } from '@aitube/timeline' +import { + CallWrapper, + ComfyApi, + PromptBuilder, + TSamplerName, + TSchedulerName, +} from '@saintno/comfyui-sdk' + +import { getWorkflowInputValues } from '../getWorkflowInputValues' + +export async function resolveSegment( + request: ResolveRequest +): Promise { + if (!request.settings.comfyUiClientId) { + throw new Error(`Missing client id for "ComfyUI"`) + } + + const segment: TimelineSegment = { ...request.segment } + + // for API doc please see: + // https://github.com/tctien342/comfyui-sdk/blob/main/examples/example-t2i.ts + const api = new ComfyApi( + request.settings.comfyUiApiUrl || 'http://localhost:8189' + ).init() + + if (request.segment.category === ClapSegmentCategory.STORYBOARD) { + + const comfyApiWorkflow = JSON.parse( + request.settings.imageGenerationWorkflow.data + ) + + const txt2ImgPrompt = new PromptBuilder( + comfyApiWorkflow, + // TODO: this list should be detect/filled automatically (see line 86) + [ + 'positive', + 'negative', + 'checkpoint', + 'seed', + 'batch', + 'step', + 'cfg', + 'sampler', + 'sheduler', + 'width', + 'height', + ], + // TODO: this list should be detect/filled automatically (see line 86) + ['images'] + ) + // TODO: those input sets should be detect/filled automatically (see line 86) + .setInputNode('checkpoint', '4.inputs.ckpt_name') + .setInputNode('seed', '3.inputs.seed') + .setInputNode('batch', '5.inputs.batch_size') + .setInputNode('negative', '7.inputs.text') + .setInputNode('positive', '6.inputs.text') + .setInputNode('cfg', '3.inputs.cfg') + .setInputNode('sampler', '3.inputs.sampler_name') + .setInputNode('sheduler', '3.inputs.scheduler') + .setInputNode('step', '3.inputs.steps') + .setInputNode('width', '5.inputs.width') + .setInputNode('height', '5.inputs.height') + .setOutputNode('images', '9') + + const workflow = txt2ImgPrompt + // TODO: this mapping should be detect/filled automatically (see line 86) + .input('checkpoint', 'SDXL/realvisxlV40_v40LightningBakedvae.safetensors') + .input('seed', generateSeed()) + .input('step', 6) + .input('cfg', 1) + .input('sampler', 'dpmpp_2m_sde_gpu') + .input('sheduler', 'sgm_uniform') + .input('width', request.meta.width) + .input('height', request.meta.height) + .input('batch', 1) + .input('positive', request.prompts.image.positive) + + // for the moment we only have non-working "mock" sample code, + // to fully implement the comfyui client, we need to work on a system + // to automatically detect the architecture of the workflow, its parameters, + // the default values, and fill them + // + // to make things easier, we are going to assume that the ClapWorkflow object + // is 100% correctly defined, and that we can rely on `inputFields` and `inputValues` + // + // that way, the responsibility of automatically identifying the inputs from a raw JSON workflow + // (eg. coming from OpenArt.ai) will be done by a separate pre-processing code + + const inputFields = + request.settings.imageGenerationWorkflow.inputFields || [] + + // since this is a random "wild" workflow, it is possible + // that the field name is a bit different + // we try to look into the workflow input fields + // to find the best match + const promptFields = [ + inputFields.find((f) => f.id === 'prompt'), // exactMatch, + inputFields.find((f) => f.id.includes('prompt')), // similarName, + inputFields.find((f) => f.type === 'string'), // similarType + ].filter((x) => typeof x !== 'undefined') + + const promptField = promptFields[0] + if (!promptField) { + throw new Error( + `this workflow doesn't seem to have a parameter called "prompt"` + ) + } + + // TODO: modify the serialized workflow payload + // to inject our params: + // ...getWorkflowInputValues(request.settings.imageGenerationWorkflow), + // [promptField.id]: request.prompts.image.positive, + + const pipeline = new CallWrapper(api, workflow) + .onPending(() => console.log('Task is pending')) + .onStart(() => console.log('Task is started')) + .onPreview((blob) => console.log(blob)) + .onFinished((data) => { + console.log( + data.images?.images.map((img: any) => api.getPathImage(img)) + ) + }) + .onProgress((info) => + console.log('Processing node', info.node, `${info.value}/${info.max}`) + ) + .onFailed((err) => console.log('Task is failed', err)) + + const result = await pipeline.run() + + console.log(`result:`, result) + } else { + throw new Error( + `Clapper doesn't support ${request.segment.category} generation for provider "ComfyUI". Please open a pull request with (working code) to solve this!` + ) + } + + return segment +} diff --git a/src/app/api/resolve/providers/comfy/temporary_demo.json b/src/app/api/resolve/providers/comfy/temporary_demo.json new file mode 100644 index 00000000..589190ff --- /dev/null +++ b/src/app/api/resolve/providers/comfy/temporary_demo.json @@ -0,0 +1,80 @@ +{ + "3": { + "inputs": { + "seed": 303533494192794, + "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", + "_meta": { + "title": "KSampler" + } + }, + "4": { + "inputs": { + "ckpt_name": "SD15/counterfeitV30_v30.safetensors" + }, + "class_type": "CheckpointLoaderSimple", + "_meta": { + "title": "Load Checkpoint" + } + }, + "5": { + "inputs": { + "width": 512, + "height": 512, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage", + "_meta": { + "title": "Empty Latent Image" + } + }, + "6": { + "inputs": { + "text": "beautiful scenery nature glass bottle landscape, , purple galaxy bottle,", + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode", + "_meta": { + "title": "CLIP Text Encode (Prompt)" + } + }, + "7": { + "inputs": { + "text": "text, watermark", + "clip": ["4", 1] + }, + "class_type": "CLIPTextEncode", + "_meta": { + "title": "CLIP Text Encode (Prompt)" + } + }, + "8": { + "inputs": { + "samples": ["3", 0], + "vae": ["4", 2] + }, + "class_type": "VAEDecode", + "_meta": { + "title": "VAE Decode" + } + }, + "9": { + "inputs": { + "filename_prefix": "ComfyUI", + "images": ["8", 0] + }, + "class_type": "SaveImage", + "_meta": { + "title": "Save Image" + } + } +} diff --git a/src/app/api/resolve/providers/falai/index.ts b/src/app/api/resolve/providers/falai/index.ts index 957d597f..c4767b51 100644 --- a/src/app/api/resolve/providers/falai/index.ts +++ b/src/app/api/resolve/providers/falai/index.ts @@ -9,6 +9,7 @@ import { FalAiVideoResponse, } from './types' import { getWorkflowInputValues } from '../getWorkflowInputValues' +import { sampleVoice } from '@/lib/core/constants' export async function resolveSegment( request: ResolveRequest @@ -55,6 +56,9 @@ export async function resolveSegment( ) // for the moment let's use FAL's predefined sizes + // but I've notived that with Flux.1 release they started to add + // support for custom sizes as well + const imageSize = request.meta.orientation === ClapMediaOrientation.SQUARE ? FalAiImageSize.SQUARE_HD @@ -63,6 +67,7 @@ export async function resolveSegment( : FalAiImageSize.LANDSCAPE_16_9 // for the moment let's use FAL's predefined sizes + // but see my previous comment above: it's temporary delete workflowDefaultValues.width delete workflowDefaultValues.height delete workflowValues.width @@ -183,11 +188,7 @@ export async function resolveSegment( } else if (request.segment.category === ClapSegmentCategory.DIALOGUE) { model = request.settings.voiceGenerationWorkflow.data || '' - let voiceIdentity = - request.prompts.voice.identity || - // TODO for the default we should use one of our own voices instea - // PS: are you implementing this task? please do a search in the code for speakers/bria.mp3 - 'https://cdn.themetavoice.xyz/speakers/bria.mp3' + let voiceIdentity = request.prompts.voice.identity || sampleVoice const result = (await fal.run(model, { input: { diff --git a/src/app/api/resolve/providers/replicate/index.ts b/src/app/api/resolve/providers/replicate/index.ts index 0cb20fbb..d3cfe32e 100644 --- a/src/app/api/resolve/providers/replicate/index.ts +++ b/src/app/api/resolve/providers/replicate/index.ts @@ -23,25 +23,25 @@ export async function resolveSegment( // this mapping isn't great, we should use something auto-adapting // like we are doing for Hugging Face (match the fields etc) if (request.segment.category === ClapSegmentCategory.STORYBOARD) { - let params: object = {} + let params: object = { + prompt: request.prompts.image.positive, + width: request.meta.width, + height: request.meta.height, + } if ( request.settings.imageGenerationWorkflow.data === 'fofr/pulid-lightning' ) { params = { - prompt: request.prompts.image.positive, + ...params, face_image: request.prompts.image.identity, } } else if ( request.settings.imageGenerationWorkflow.data === 'zsxkib/pulid' ) { params = { - prompt: request.prompts.image.positive, + ...params, main_face_image: request.prompts.image.identity, } - } else { - params = { - prompt: request.prompts.image.positive, - } } const response = (await replicate.run( request.settings.imageGenerationWorkflow as any, diff --git a/src/components/core/providers/logos.ts b/src/components/core/providers/logos.ts index a369c399..0eea35a4 100644 --- a/src/components/core/providers/logos.ts +++ b/src/components/core/providers/logos.ts @@ -1,6 +1,8 @@ import { ClapWorkflowProvider } from '@aitube/clap' const none = '/images/providers/none.png' +const builtin = '/images/providers/none.png' // <-- TODO put Clapper logo here +const comfyui = '/images/providers/none.png' // <-- TODO put ComfyUI logo here const anthropic = '/images/providers/anthropic.png' const cohere = '/images/providers/cohere.png' const comfyicu = '/images/providers/comfyicu.png' @@ -31,7 +33,8 @@ const udio = '/images/providers/udio.png' export const ClapWorkflowProvidersLogos: Record = { [ClapWorkflowProvider.NONE]: none, - [ClapWorkflowProvider.BUILTIN]: none, // <- TODO: use Clapper logo here + [ClapWorkflowProvider.BUILTIN]: builtin, + [ClapWorkflowProvider.COMFYUI]: comfyui, [ClapWorkflowProvider.ANTHROPIC]: anthropic, [ClapWorkflowProvider.COHERE]: cohere, [ClapWorkflowProvider.ELEVENLABS]: elevenlabs, diff --git a/src/components/settings/provider.tsx b/src/components/settings/provider.tsx index 11388aa1..f851b257 100644 --- a/src/components/settings/provider.tsx +++ b/src/components/settings/provider.tsx @@ -36,6 +36,12 @@ export function SettingsSectionProvider() { const replicateApiKey = useSettings((s) => s.replicateApiKey) const setReplicateApiKey = useSettings((s) => s.setReplicateApiKey) + const comfyUiApiUrl = useSettings((s) => s.comfyUiApiUrl) + const setComfyUiApiUrl = useSettings((s) => s.setComfyUiApiUrl) + + const comfyUiClientId = useSettings((s) => s.comfyUiClientId) + const setComfyUiClientId = useSettings((s) => s.setComfyUiClientId) + const comfyIcuApiKey = useSettings((s) => s.comfyIcuApiKey) const setComfyIcuApiKey = useSettings((s) => s.setComfyIcuApiKey) @@ -112,6 +118,23 @@ export function SettingsSectionProvider() { onChange={setReplicateApiKey} type={apiKeyType} /> + + + + + ()( (set, get) => ({ ...getDefaultSettingsState(), - setCustomComfyUiApiKey: (customComfyUiApiKey?: string) => { - const { customComfyUiApiKey: defaulCustomComfyUiApiKey } = + setComfyUiClientId: (comfyUiClientId?: string) => { + const { comfyUiClientId: defaultComfyUiClientId } = getDefaultSettingsState() set({ - customComfyUiApiKey: getValidString( - customComfyUiApiKey, - defaulCustomComfyUiApiKey + comfyUiClientId: getValidString( + comfyUiClientId, + defaultComfyUiClientId ), }) }, @@ -610,6 +610,13 @@ export const useSettings = create()( ), }) }, + setComfyUiApiUrl: (comfyUiApiUrl?: string) => { + const { comfyUiApiUrl: defaultComfyUiApiUrl } = + getDefaultSettingsState() + set({ + comfyUiApiUrl: getValidString(comfyUiApiUrl, defaultComfyUiApiUrl), + }) + }, setGradioApiUrlForAssistant: (gradioApiUrlForAssistant?: string) => { set({ gradioApiUrlForAssistant: getValidString( @@ -948,8 +955,8 @@ export const useSettings = create()( // why do we need those fallbacks? because some users will leave the fields empty, // eg. an empty model string.. basically we want to allow empty config that still works! - customComfyUiApiKey: - state.customComfyUiApiKey || defaultSettings.customComfyUiApiKey, + comfyUiClientId: + state.comfyUiClientId || defaultSettings.comfyUiClientId, replicateApiKey: state.replicateApiKey || defaultSettings.replicateApiKey, comfyIcuApiKey: @@ -1080,6 +1087,9 @@ export const useSettings = create()( comfyWorkflowForMusic: state.comfyWorkflowForMusic || defaultSettings.comfyWorkflowForMusic, + + comfyUiApiUrl: state.comfyUiApiUrl || defaultSettings.comfyUiApiUrl, + gradioApiUrlForAssistant: state.gradioApiUrlForAssistant || defaultSettings.gradioApiUrlForAssistant,