From de6c90cbc42daef30dbf0911ae0a554edc968dea Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sun, 1 Sep 2024 01:50:21 +0900 Subject: [PATCH] Add fal.ai support --- src/lang/en.ts | 3 +- src/lib/Setting/Pages/OtherBotSettings.svelte | 29 +++++++++ src/lib/SideBars/CharConfig.svelte | 2 +- src/ts/process/stableDiff.ts | 63 +++++++++++++++++++ src/ts/process/tts.ts | 2 +- src/ts/storage/database.ts | 7 +++ src/ts/storage/globalApi.ts | 3 +- 7 files changed, 105 insertions(+), 4 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index ff575a56..62e17528 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -134,7 +134,7 @@ export const languageEnglish = { legacyTranslation: "If enabled, it will use the old translation method, which preprocess markdown and quotes before translations instead of postprocessing after translations.", luaHelp: "You can use Lua scripts as a trigger script. you can define onInput, onOutput, onStart functions. onInput is called when user sends a message, onOutput is called when character sends a message, onStart is called when the chat starts. for more information, see the documentation.", claudeCachingExperimental: "Caching in Claude is experimental feature that can reduce the cost of the model, but it can also increase the cost if you use it without reroll. since this is a experimental feature, it can be unstable and behavior can be changed in the future.", - + urllora: "You can use direct download link of the model file. you can make direct url from google drive like website like https://sites.google.com/site/gdocs2direct/ , or use civitai URL, copy the the AIR (looks like `urn:air:flux1:lora:civitai:180891@776656` or just `civitai:180891@776656`) and paste it.", }, setup: { chooseProvider: "Choose AI Provider", @@ -688,4 +688,5 @@ export const languageEnglish = { hideApiKeys: "Hide API Keys", unformatQuotes: "Disable Quote Formatting", enableDevTools: "Enable Dev Tools", + selectFile: "Select File" } \ No newline at end of file diff --git a/src/lib/Setting/Pages/OtherBotSettings.svelte b/src/lib/Setting/Pages/OtherBotSettings.svelte index 94b665e5..44a542ac 100644 --- a/src/lib/Setting/Pages/OtherBotSettings.svelte +++ b/src/lib/Setting/Pages/OtherBotSettings.svelte @@ -76,6 +76,7 @@ Dall-E Stability API ComfyUI + Fal.ai {#if $DataBase.sdProvider === 'webui'} @@ -295,6 +296,34 @@ Timeout (sec) {/if} + + {#if $DataBase.sdProvider === 'fal'} + Fal.ai API Key + + + Width + + Height + + + Model + + Flux[Dev] + Flux[Dev] with Lora + Flux[Pro] + Flux[Schnell] + + + {#if $DataBase.falModel === 'fal-ai/flux-lora'} + Lora Model URL + + + Lora Weight + + {/if} + + + {/if} {/if} diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte index a35b1b5f..162787cf 100644 --- a/src/lib/SideBars/CharConfig.svelte +++ b/src/lib/SideBars/CharConfig.svelte @@ -877,7 +877,7 @@ className="h-10"> {#if currentChar.data.gptSoVitsConfig.ref_audio_data.assetId === '' || currentChar.data.gptSoVitsConfig.ref_audio_data.assetId === undefined} - Select File + {language.selectFile} {:else} {currentChar.data.gptSoVitsConfig.ref_audio_data.fileName} {/if} diff --git a/src/ts/process/stableDiff.ts b/src/ts/process/stableDiff.ts index d95e9226..87e4120c 100644 --- a/src/ts/process/stableDiff.ts +++ b/src/ts/process/stableDiff.ts @@ -523,5 +523,68 @@ export async function generateAIImage(genPrompt:string, currentChar:character, n return returnSdData } + if(db.sdProvider === 'fal'){ + const model = db.falModel + const token = db.falToken + + let body:{[key:string]:any} = { + prompt: genPrompt, + enable_safety_checker: false, + sync_mode: true, + image_size: { + "width": db.sdConfig.width, + "height": db.sdConfig.height, + } + } + + if(db.falModel === 'fal-ai/flux-lora'){ + let loraPath = db.falLora + if(loraPath.startsWith('urn:') || loraPath.startsWith('civitai:')){ + const id = loraPath.split('@').pop() + loraPath = `https://civitai.com/api/download/models/${id}?type=Model&format=SafeTensor` + } + body.loras = [{ + "path": loraPath, + "scale": db.falLoraScale + }] + } + + if(db.falModel === 'fal-ai/flux-pro'){ + delete body.enable_safety_checker + } + console.log(body) + + const res = await globalFetch('https://fal.run/' + model, { + headers: { + "Authorization": "Key " + token, + "Content-Type": "application/json" + }, + method: 'POST', + body: body + }) + + console.log(res) + + if(!res.ok){ + alertError(JSON.stringify(res.data)) + return false + } + + let image = res.data?.images?.[0]?.url + if(!image){ + alertError(JSON.stringify(res.data)) + return false + } + + if(returnSdData === 'inlay'){ + return image + } + else{ + let charemotions = get(CharEmotion) + const emos:[string, string,number][] = [[image, image, Date.now()]] + charemotions[currentChar.chaId] = emos + CharEmotion.set(charemotions) + } + } return '' } diff --git a/src/ts/process/tts.ts b/src/ts/process/tts.ts index 57a4dbfe..18b39caf 100644 --- a/src/ts/process/tts.ts +++ b/src/ts/process/tts.ts @@ -267,7 +267,7 @@ export async function sayTTS(character:character,text:string) { 'Content-Type': 'application/json' }, rawResponse: false, - + plainFetchDeforce: true, }) console.log(path) if(path.ok){ diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index aefc7686..b277f7bc 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -432,6 +432,8 @@ export function setDatabase(data:Database){ data.unformatQuotes ??= false data.ttsAutoSpeech ??= false data.translatorInputLanguage ??= 'auto' + data.falModel ??= 'fal-ai/flux/dev' + data.falLoraScale ??= 1 changeLanguage(data.language) DataBase.set(data) } @@ -718,6 +720,11 @@ export interface Database{ hideApiKey: boolean unformatQuotes: boolean enableDevTools: boolean + falToken: string + falModel: string + falLora: string + falLoraName: string + falLoraScale: number } export interface customscript{ diff --git a/src/ts/storage/globalApi.ts b/src/ts/storage/globalApi.ts index 20c37e1c..f06b96e2 100644 --- a/src/ts/storage/globalApi.ts +++ b/src/ts/storage/globalApi.ts @@ -577,6 +577,7 @@ const knownHostes = ["localhost","127.0.0.1","0.0.0.0"] interface GlobalFetchArgs { plainFetchForce?: boolean; + plainFetchDeforce?: boolean; body?: any; headers?: { [key: string]: string }; rawResponse?: boolean; @@ -625,7 +626,7 @@ export async function globalFetch(url: string, arg: GlobalFetchArgs = {}): Promi if (arg.abortSignal?.aborted) { return { ok: false, data: 'aborted', headers: {} }} const urlHost = new URL(url).hostname - const forcePlainFetch = (knownHostes.includes(urlHost) && !isTauri) || db.usePlainFetch || arg.plainFetchForce + const forcePlainFetch = ((knownHostes.includes(urlHost) && !isTauri) || db.usePlainFetch || arg.plainFetchForce) && !arg.plainFetchDeforce if (knownHostes.includes(urlHost) && !isTauri && !isNodeServer){ return { ok: false, headers: {}, data: 'You are trying local request on web version. This is not allowed due to browser security policy. Use the desktop version instead, or use a tunneling service like ngrok and set the CORS to allow all.' }