feat: add comfyUI support (#579)
# PR Checklist - [ ] Did you check if it works normally in all models? *ignore this when it dosen't uses models* - [ ] Did you check if it works normally in all of web, local and node hosted versions? if it dosen't, did you blocked it in those versions? - Due to issues with my development environment, I only tested in Node, but it is expected to work in other environments as well. - [x] Did you add a type def? # Description Added support for image generation using ComfyUI
This commit is contained in:
@@ -46,6 +46,7 @@
|
|||||||
<OptionInput value="novelai" >Novel AI</OptionInput>
|
<OptionInput value="novelai" >Novel AI</OptionInput>
|
||||||
<OptionInput value="dalle" >Dall-E</OptionInput>
|
<OptionInput value="dalle" >Dall-E</OptionInput>
|
||||||
<OptionInput value="stability" >Stability API</OptionInput>
|
<OptionInput value="stability" >Stability API</OptionInput>
|
||||||
|
<OptionInput value="comfy" >ComfyUI</OptionInput>
|
||||||
</SelectInput>
|
</SelectInput>
|
||||||
|
|
||||||
{#if $DataBase.sdProvider === 'webui'}
|
{#if $DataBase.sdProvider === 'webui'}
|
||||||
@@ -243,6 +244,28 @@
|
|||||||
</SelectInput>
|
</SelectInput>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if $DataBase.sdProvider === 'comfy'}
|
||||||
|
<span class="text-draculared text-xs mb-2">The first image generated by the prompt will be selected. </span>
|
||||||
|
{#if !isTauri}
|
||||||
|
<span class="text-draculared text-xs mb-2">"Please run comfyUI with --enable-cors-header."</span>
|
||||||
|
{/if}
|
||||||
|
<span class="text-textcolor mt-2">ComfyUI {language.providerURL}</span>
|
||||||
|
<TextInput size="sm" marginBottom placeholder="http://127.0.0.1:8188" bind:value={$DataBase.comfyUiUrl}/>
|
||||||
|
<span class="text-textcolor">Workflow</span>
|
||||||
|
<TextInput size="sm" marginBottom placeholder="valid ComfyUI API json (Enable Dev mode Options in ComfyUI)" bind:value={$DataBase.comfyConfig.workflow}/>
|
||||||
|
|
||||||
|
<span class="text-textcolor">Positive Text Node: ID</span>
|
||||||
|
<TextInput size="sm" marginBottom placeholder="eg. 1, 3, etc" bind:value={$DataBase.comfyConfig.posNodeID}/>
|
||||||
|
<span class="text-textcolor">Positive Text Node: Input Field Name</span>
|
||||||
|
<TextInput size="sm" marginBottom placeholder="eg. text" bind:value={$DataBase.comfyConfig.posInputName}/>
|
||||||
|
<span class="text-textcolor">Negative Text Node: ID</span>
|
||||||
|
<TextInput size="sm" marginBottom placeholder="eg. 1, 3, etc" bind:value={$DataBase.comfyConfig.negNodeID}/>
|
||||||
|
<span class="text-textcolor">Positive Text Node: Input Field Name</span>
|
||||||
|
<TextInput size="sm" marginBottom placeholder="eg. text" bind:value={$DataBase.comfyConfig.negInputName}/>
|
||||||
|
<span class="text-textcolor">Timeout (sec)</span>
|
||||||
|
<NumberInput size="sm" marginBottom bind:value={$DataBase.comfyConfig.timeout} min={1} max={120} />
|
||||||
|
{/if}
|
||||||
</Arcodion>
|
</Arcodion>
|
||||||
|
|
||||||
<Arcodion name="TTS" styled>
|
<Arcodion name="TTS" styled>
|
||||||
@@ -370,4 +393,4 @@
|
|||||||
<Check bind:check={$DataBase.hypaMemory} name={language.enable + ' ' + language.HypaMemory}/>
|
<Check bind:check={$DataBase.hypaMemory} name={language.enable + ' ' + language.HypaMemory}/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</Arcodion>
|
</Arcodion>
|
||||||
|
|||||||
@@ -413,6 +413,80 @@ export async function generateAIImage(genPrompt:string, currentChar:character, n
|
|||||||
return returnSdData
|
return returnSdData
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
if(db.sdProvider === 'comfy'){
|
||||||
|
const {workflow, posNodeID, posInputName, negNodeID, negInputName} = db.comfyConfig
|
||||||
|
const baseUrl = new URL(db.comfyUiUrl)
|
||||||
|
|
||||||
|
const createUrl = (pathname: string, params: Record<string, string> = {}) => {
|
||||||
|
const url = new URL(pathname, baseUrl)
|
||||||
|
url.search = new URLSearchParams(params).toString()
|
||||||
|
return url.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchWrapper = async (url: string, options = {}) => {
|
||||||
|
console.log(url)
|
||||||
|
const response = await globalFetch(url, options)
|
||||||
|
if (!response.ok) {
|
||||||
|
console.log(JSON.stringify(response.data))
|
||||||
|
throw new Error(JSON.stringify(response.data))
|
||||||
|
}
|
||||||
|
return response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const prompt = JSON.parse(workflow)
|
||||||
|
prompt[posNodeID].inputs[posInputName] = genPrompt
|
||||||
|
prompt[negNodeID].inputs[negInputName] = neg
|
||||||
|
|
||||||
|
const { prompt_id: id } = await fetchWrapper(createUrl('/prompt'), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: { 'prompt': prompt }
|
||||||
|
})
|
||||||
|
console.log(`prompt id: ${id}`)
|
||||||
|
|
||||||
|
let item
|
||||||
|
|
||||||
|
const startTime = Date.now()
|
||||||
|
const timeout = db.comfyConfig.timeout * 1000
|
||||||
|
while (!(item = (await (await fetch(createUrl('/history'), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
method: 'GET'})).json())[id])) {
|
||||||
|
console.log("Checking /history...")
|
||||||
|
if (Date.now() - startTime >= timeout) {
|
||||||
|
alertError("Error: Image generation took longer than expected.");
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
await new Promise(r => setTimeout(r, 1000))
|
||||||
|
} // Check history until the generation is complete.
|
||||||
|
const genImgInfo = Object.values(item.outputs).flatMap((output: any) => output.images)[0];
|
||||||
|
|
||||||
|
const imgResponse = await fetch(createUrl('/view', {
|
||||||
|
filename: genImgInfo.filename,
|
||||||
|
subfolder: genImgInfo.subfolder,
|
||||||
|
type: genImgInfo.type
|
||||||
|
}), {
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
method: 'GET'})
|
||||||
|
const img64 = Buffer.from(await imgResponse.arrayBuffer()).toString('base64')
|
||||||
|
|
||||||
|
if(returnSdData === 'inlay'){
|
||||||
|
return `data:image/png;base64,${img64}`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let charemotions = get(CharEmotion)
|
||||||
|
const img = `data:image/png;base64,${img64}`
|
||||||
|
const emos:[string, string,number][] = [[img, img, Date.now()]]
|
||||||
|
charemotions[currentChar.chaId] = emos
|
||||||
|
CharEmotion.set(charemotions)
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnSdData
|
||||||
|
} catch (error) {
|
||||||
|
alertError(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -419,6 +419,16 @@ export function setDatabase(data:Database){
|
|||||||
data.stabilityModel ??= 'sd3-large'
|
data.stabilityModel ??= 'sd3-large'
|
||||||
data.stabllityStyle ??= ''
|
data.stabllityStyle ??= ''
|
||||||
data.legacyTranslation ??= false
|
data.legacyTranslation ??= false
|
||||||
|
data.comfyUiUrl ??= 'http://localhost:8188'
|
||||||
|
data.comfyConfig ??= {
|
||||||
|
workflow: '',
|
||||||
|
posNodeID: '',
|
||||||
|
posInputName: 'text',
|
||||||
|
negNodeID: '',
|
||||||
|
negInputName: 'text',
|
||||||
|
timeout: 30
|
||||||
|
}
|
||||||
|
|
||||||
changeLanguage(data.language)
|
changeLanguage(data.language)
|
||||||
DataBase.set(data)
|
DataBase.set(data)
|
||||||
}
|
}
|
||||||
@@ -695,6 +705,8 @@ export interface Database{
|
|||||||
stabilityKey: string
|
stabilityKey: string
|
||||||
stabllityStyle: string
|
stabllityStyle: string
|
||||||
legacyTranslation: boolean
|
legacyTranslation: boolean
|
||||||
|
comfyConfig: ComfyConfig
|
||||||
|
comfyUiUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface customscript{
|
export interface customscript{
|
||||||
@@ -967,6 +979,16 @@ interface NAIImgConfig{
|
|||||||
InfoExtracted:number,
|
InfoExtracted:number,
|
||||||
RefStrength:number
|
RefStrength:number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ComfyConfig{
|
||||||
|
workflow:string,
|
||||||
|
posNodeID: string,
|
||||||
|
posInputName:string,
|
||||||
|
negNodeID: string,
|
||||||
|
negInputName:string,
|
||||||
|
timeout: number
|
||||||
|
}
|
||||||
|
|
||||||
export type FormatingOrderItem = 'main'|'jailbreak'|'chats'|'lorebook'|'globalNote'|'authorNote'|'lastChat'|'description'|'postEverything'|'personaPrompt'
|
export type FormatingOrderItem = 'main'|'jailbreak'|'chats'|'lorebook'|'globalNote'|'authorNote'|'lastChat'|'description'|'postEverything'|'personaPrompt'
|
||||||
|
|
||||||
export interface Chat{
|
export interface Chat{
|
||||||
@@ -1486,4 +1508,4 @@ export async function importPreset(f:{
|
|||||||
pre.name ??= "Imported"
|
pre.name ??= "Imported"
|
||||||
db.botPresets.push(pre)
|
db.botPresets.push(pre)
|
||||||
setDatabase(db)
|
setDatabase(db)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user