[feat] added inlay emotion/imggen & rework imggen

This commit is contained in:
kwaroran
2023-11-27 21:50:43 +09:00
parent d71ef1b8a2
commit 1d15eacd72
12 changed files with 428 additions and 340 deletions

View File

@@ -453,4 +453,8 @@ export const languageEnglish = {
joinMultiUserRoom: "Join MultiUser Room", joinMultiUserRoom: "Join MultiUser Room",
exactTokens: "Exact Tokens", exactTokens: "Exact Tokens",
fixedTokens: "Approximate Tokens", fixedTokens: "Approximate Tokens",
inlayViewScreen: "Inlay Screen",
imgGenPrompt: "Image Generation Prompt",
imgGenNegatives: "Image Generation Negative Prompt",
imgGenInstructions: "Image Generation Instructions",
} }

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { ArrowLeft, ArrowRight, EditIcon, LanguagesIcon, RefreshCcwIcon, TrashIcon, CopyIcon } from "lucide-svelte"; import { ArrowLeft, ArrowRight, PencilIcon, LanguagesIcon, RefreshCcwIcon, TrashIcon, CopyIcon } from "lucide-svelte";
import { ParseMarkdown, type simpleCharacterArgument } from "../../ts/parser"; import { ParseMarkdown, type simpleCharacterArgument } from "../../ts/parser";
import AutoresizeArea from "../UI/GUI/TextAreaResizable.svelte"; import AutoresizeArea from "../UI/GUI/TextAreaResizable.svelte";
import { alertConfirm, alertError } from "../../ts/alert"; import { alertConfirm, alertError } from "../../ts/alert";
@@ -154,7 +154,7 @@
edit() edit()
} }
}}> }}>
<EditIcon size={20}/> <PencilIcon size={20}/>
</button> </button>
<button class="ml-2 hover:text-green-500 transition-colors" on:click={rm}> <button class="ml-2 hover:text-green-500 transition-colors" on:click={rm}>
<TrashIcon size={20}/> <TrashIcon size={20}/>

View File

@@ -27,6 +27,8 @@
import OptionInput from "../UI/GUI/OptionInput.svelte"; import OptionInput from "../UI/GUI/OptionInput.svelte";
import RegexList from "./Scripts/RegexList.svelte"; import RegexList from "./Scripts/RegexList.svelte";
import TriggerList from "./Scripts/TriggerList.svelte"; import TriggerList from "./Scripts/TriggerList.svelte";
import CheckInput from "../UI/GUI/CheckInput.svelte";
import { updateInlayScreen } from "src/ts/process/inlayScreen";
let subMenu = 0 let subMenu = 0
@@ -319,7 +321,11 @@
<!-- svelte-ignore empty-block --> <!-- svelte-ignore empty-block -->
{#if currentChar.type !== 'group'} {#if currentChar.type !== 'group'}
<SelectInput className="mb-2" bind:value={currentChar.data.viewScreen}> <SelectInput className="mb-2" bind:value={currentChar.data.viewScreen} on:change={() => {
if(currentChar.type === 'character'){
currentChar.data = updateInlayScreen(currentChar.data)
}
}}>
<OptionInput value="none">{language.none}</OptionInput> <OptionInput value="none">{language.none}</OptionInput>
<OptionInput value="emotion">{language.emotionImage}</OptionInput> <OptionInput value="emotion">{language.emotionImage}</OptionInput>
<OptionInput value="imggen">{language.imageGeneration}</OptionInput> <OptionInput value="imggen">{language.imageGeneration}</OptionInput>
@@ -383,73 +389,34 @@
<span>Loading...</span> <span>Loading...</span>
{/if} {/if}
</div> </div>
{#if currentChar.data.inlayViewScreen}
<span class="text-textcolor mt-2">{language.imgGenInstructions}</span>
<TextAreaInput bind:value={currentChar.data.newGenData.emotionInstructions} />
{/if}
<CheckInput bind:check={currentChar.data.inlayViewScreen} name={language.inlayViewScreen} onChange={() => {
if(currentChar.type === 'character'){
currentChar.data = updateInlayScreen(currentChar.data)
}
}}/>
{/if} {/if}
{#if currentChar.data.viewScreen === 'imggen'} {#if currentChar.data.viewScreen === 'imggen'}
<span class="text-textcolor mt-6">{language.imageGeneration} <Help key="imggen"/></span> <span class="text-textcolor mt-6">{language.imageGeneration} <Help key="imggen"/></span>
<span class="text-textcolor2 text-xs">{language.emotionWarn}</span> <span class="text-textcolor2 text-xs">{language.emotionWarn}</span>
<div class="w-full max-w-full border border-selected rounded-md p-2"> <span class="text-textcolor mt-2">{language.imgGenPrompt}</span>
<table class="w-full max-w-full tabler"> <TextAreaInput bind:value={currentChar.data.newGenData.prompt} />
<tr> <span class="text-textcolor mt-2">{language.imgGenNegatives}</span>
<th class="font-medium w-1/3">{language.key}</th> <TextAreaInput bind:value={currentChar.data.newGenData.negative} />
<th class="font-medium w-1/2">{language.value}</th> <span class="text-textcolor mt-2">{language.imgGenInstructions}</span>
<th class="font-medium"></th> <TextAreaInput bind:value={currentChar.data.newGenData.instructions} />
</tr>
{#if currentChar.data.sdData.length === 0} <CheckInput bind:check={currentChar.data.inlayViewScreen} name={language.inlayViewScreen} onChange={() => {
<tr> if(currentChar.type === 'character'){
<div class="text-textcolor2">{language.noData}</div> currentChar.data = updateInlayScreen(currentChar.data)
</tr>
{/if}
{#each currentChar.data.sdData as emo, i}
<tr>
<td class="font-medium truncate w-1/3">
<TextInput size="sm" marginBottom bind:value={currentChar.data.sdData[i][0]} />
</td>
<td class="font-medium truncate w-1/2">
<TextInput size="sm" marginBottom bind:value={currentChar.data.sdData[i][1]} />
</td>
{#if (!['always','negative'].includes(currentChar.data.sdData[i][0]))}
<button class="font-medium flex justify-center items-center h-full cursor-pointer hover:text-green-500" on:click={() => {
let db = ($DataBase)
let charId = $selectedCharID
let dbChar = db.characters[charId]
if(dbChar.type !== 'group'){
dbChar.sdData.splice(i, 1)
db.characters[charId] = dbChar
} }
$DataBase = (db) }}/>
}}><TrashIcon /></button>
{:else}
<td></td>
{/if}
</tr>
{/each}
</table>
</div>
<div class="text-textcolor2 hover:text-textcolor mt-2 flex">
{#if !$addingEmotion}
<button class="cursor-pointer hover:text-green-500" on:click={() => {
let db = ($DataBase)
let charId = $selectedCharID
let dbChar = db.characters[charId]
if(dbChar.type !== 'group'){
dbChar.sdData.push(['', ''])
db.characters[charId] = dbChar
}
$DataBase = (db)
}}>
<PlusIcon />
</button>
{:else}
<span>Loading...</span>
{/if}
</div>
<span class="text-textcolor mt-6">{language.currentImageGeneration}</span>
{#if currentChar.data.chats[currentChar.data.chatPage].sdData}
<TextAreaInput margin="both" autocomplete="off" bind:value={currentChar.data.chats[currentChar.data.chatPage].sdData}></TextAreaInput>
{:else}
<span><div class="text-textcolor2">{language.noData}</div></span>
{/if}
{/if} {/if}
{:else if subMenu === 3} {:else if subMenu === 3}
<h2 class="mb-2 text-2xl font-bold mt-2">{language.loreBook} <Help key="lorebook"/></h2> <h2 class="mb-2 text-2xl font-bold mt-2">{language.loreBook} <Help key="lorebook"/></h2>

View File

@@ -7,7 +7,7 @@
import VisualNovelChat from "./VisualNovelChat.svelte"; import VisualNovelChat from "./VisualNovelChat.svelte";
const wallPaper = `background-image: url(${defaultWallpaper})` const wallPaper = `background-image: url(${defaultWallpaper})`
export let forceRender:() => void let forceRender:() => void
let bgImg= '' let bgImg= ''
let lastBg = '' let lastBg = ''
$: (async () =>{ $: (async () =>{

View File

@@ -9,6 +9,7 @@ import { selectedCharID } from "./stores";
import { checkCharOrder, downloadFile, getFileSrc, readImage } from "./storage/globalApi"; import { checkCharOrder, downloadFile, getFileSrc, readImage } from "./storage/globalApi";
import * as yuso from 'yuso' import * as yuso from 'yuso'
import { reencodeImage } from "./image"; import { reencodeImage } from "./image";
import { updateInlayScreen } from "./process/inlayScreen";
export function createNewCharacter() { export function createNewCharacter() {
let db = get(DataBase) let db = get(DataBase)
@@ -300,6 +301,9 @@ export function characterFormatUpdate(index:number|character){
depth: 0, depth: 0,
prompt: '' prompt: ''
} }
if(!cha.newGenData){
cha = updateInlayScreen(cha)
}
} }
else{ else{
if((!cha.characterTalks) || cha.characterTalks.length !== cha.characters.length){ if((!cha.characterTalks) || cha.characterTalks.length !== cha.characters.length){

View File

@@ -24,12 +24,21 @@ export async function postInlayImage(){
const extention = img.name.split('.').at(-1) const extention = img.name.split('.').at(-1)
//darw in canvas to convert to png const imgObj = new Image()
imgObj.src = URL.createObjectURL(new Blob([img.data], {type: `image/${extention}`}))
return await writeInlayImage(imgObj, {
name: img.name,
ext: extention
})
}
export async function writeInlayImage(imgObj:HTMLImageElement, arg:{name?:string, ext?:string} = {}) {
let drawHeight = 0
let drawWidth = 0
const canvas = document.createElement('canvas') const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d') const ctx = canvas.getContext('2d')
const imgObj = new Image()
let drawHeight, drawWidth = 0
imgObj.src = URL.createObjectURL(new Blob([img.data], {type: `image/${extention}`}))
await new Promise((resolve) => { await new Promise((resolve) => {
imgObj.onload = () => { imgObj.onload = () => {
drawHeight = imgObj.height drawHeight = imgObj.height
@@ -59,9 +68,9 @@ export async function postInlayImage(){
const imgid = v4() const imgid = v4()
await inlayStorage.setItem(imgid, { await inlayStorage.setItem(imgid, {
name: img.name, name: arg.name ?? imgid,
data: dataURI, data: dataURI,
ext: extention, ext: arg.ext ?? 'png',
height: drawHeight, height: drawHeight,
width: drawWidth width: drawWidth
}) })

View File

@@ -65,7 +65,7 @@ DOMPurify.addHook("uponSanitizeAttribute", (node, data) => {
}) })
const assetRegex = /{{(raw|img|video|audio|bg)::(.+?)}}/g const assetRegex = /{{(raw|img|video|audio|bg|emotion)::(.+?)}}/g
async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|character, mode:'normal'|'back', mode2:'unset'|'pre'|'post' = 'unset'){ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|character, mode:'normal'|'back', mode2:'unset'|'pre'|'post' = 'unset'){
const db = get(DataBase) const db = get(DataBase)
@@ -74,13 +74,28 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c
if(char.additionalAssets){ if(char.additionalAssets){
let assetPaths:{[key:string]:string} = {} let assetPaths:{[key:string]:string} = {}
let emoPaths:{[key:string]:string} = {}
for(const asset of char.additionalAssets){ for(const asset of char.additionalAssets){
const assetPath = await getFileSrc(asset[1]) const assetPath = await getFileSrc(asset[1])
assetPaths[asset[0].toLocaleLowerCase()] = assetPath assetPaths[asset[0].toLocaleLowerCase()] = assetPath
} }
if(char.emotionImages){
for(const emo of char.emotionImages){
const emoPath = await getFileSrc(emo[1])
emoPaths[emo[0].toLocaleLowerCase()] = emoPath
}
}
data = data.replaceAll(assetRegex, (full:string, type:string, name:string) => { data = data.replaceAll(assetRegex, (full:string, type:string, name:string) => {
name = name.toLocaleLowerCase() name = name.toLocaleLowerCase()
if(type === 'emotion'){
console.log(emoPaths, name)
const path = emoPaths[name]
if(!path){
return ''
}
return `<img src="${path}" alt="${path}" style="${assetWidthString} "/>`
}
const path = assetPaths[name] const path = assetPaths[name]
if(!path){ if(!path){
return '' return ''
@@ -129,6 +144,7 @@ export interface simpleCharacterArgument{
customscript: customscript[] customscript: customscript[]
chaId: string, chaId: string,
virtualscript?: string virtualscript?: string
emotionImages?: [string, string][]
} }

View File

@@ -22,6 +22,7 @@ import { cipherChat, decipherChat } from "./cipherChat";
import { getInlayImage, supportsInlayImage } from "../image"; import { getInlayImage, supportsInlayImage } from "../image";
import { getGenerationModelString } from "./models/modelString"; import { getGenerationModelString } from "./models/modelString";
import { sendPeerChar } from "../sync/multiuser"; import { sendPeerChar } from "../sync/multiuser";
import { runInlayScreen } from "./inlayScreen";
export interface OpenAIChat{ export interface OpenAIChat{
role: 'system'|'user'|'assistant'|'function' role: 'system'|'user'|'assistant'|'function'
@@ -300,6 +301,21 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
}) })
} }
if(currentChar.inlayViewScreen){
if(currentChar.viewScreen === 'emotion'){
unformated.postEverything.push({
role: 'system',
content: currentChar.newGenData.emotionInstructions.replaceAll('{{slot}}', currentChar.emotionImages.map((v) => v[0]).join(', '))
})
}
if(currentChar.viewScreen === 'imggen'){
unformated.postEverything.push({
role: 'system',
content: currentChar.newGenData.instructions
})
}
}
if(lorepmt.special_act){ if(lorepmt.special_act){
unformated.postEverything.push({ unformated.postEverything.push({
role: 'system', role: 'system',
@@ -498,6 +514,10 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
} }
let inlays:string[] = [] let inlays:string[] = []
if(db.inlayImage){ if(db.inlayImage){
if(msg.role === 'char'){
formedChat = formedChat.replace(/{{inlay::(.+?)}}/g, '')
}
else{
const inlayMatch = formedChat.match(/{{inlay::(.+?)}}/g) const inlayMatch = formedChat.match(/{{inlay::(.+?)}}/g)
if(inlayMatch){ if(inlayMatch){
for(const inlay of inlayMatch){ for(const inlay of inlayMatch){
@@ -505,6 +525,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
} }
} }
} }
}
if(inlays.length > 0){ if(inlays.length > 0){
for(const inlay of inlays){ for(const inlay of inlays){
@@ -653,7 +674,6 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
}) })
} }
function pushPrompts(cha:OpenAIChat[]){ function pushPrompts(cha:OpenAIChat[]){
for(const chat of cha){ for(const chat of cha){
if(!chat.content){ if(!chat.content){
@@ -913,9 +933,17 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
currentChat = db.characters[selectedChar].chats[selectedChat] currentChat = db.characters[selectedChar].chats[selectedChat]
const triggerResult = await runTrigger(currentChar, 'output', {chat:currentChat}) const triggerResult = await runTrigger(currentChar, 'output', {chat:currentChat})
console.log(triggerResult)
if(triggerResult && triggerResult.chat){ if(triggerResult && triggerResult.chat){
db.characters[selectedChar].chats[selectedChat] = triggerResult.chat currentChat = triggerResult.chat
}
const inlayr = runInlayScreen(currentChar, currentChat.message[msgIndex].data)
currentChat.message[msgIndex].data = inlayr.text
db.characters[selectedChar].chats[selectedChat] = currentChat
setDatabase(db)
if(inlayr.promise){
const t = await inlayr.promise
currentChat.message[msgIndex].data = t
db.characters[selectedChar].chats[selectedChat] = currentChat
setDatabase(db) setDatabase(db)
} }
await sayTTS(currentChar, result) await sayTTS(currentChar, result)
@@ -938,6 +966,8 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
result2 = await processScriptFull(nowChatroom, reformatContent(beforeChat.data + mess), 'editoutput', msgIndex) result2 = await processScriptFull(nowChatroom, reformatContent(beforeChat.data + mess), 'editoutput', msgIndex)
} }
result = result2.data result = result2.data
const inlayResult = runInlayScreen(currentChar, result)
result = inlayResult.text
emoChanged = result2.emoChanged emoChanged = result2.emoChanged
if(i === 0 && arg.continue){ if(i === 0 && arg.continue){
db.characters[selectedChar].chats[selectedChat].message[msgIndex] = { db.characters[selectedChar].chats[selectedChat].message[msgIndex] = {
@@ -950,6 +980,10 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
generationId: generationId, generationId: generationId,
} }
} }
if(inlayResult.promise){
const p = await inlayResult.promise
db.characters[selectedChar].chats[selectedChat].message[msgIndex].data = p
}
} }
else{ else{
db.characters[selectedChar].chats[selectedChat].message.push({ db.characters[selectedChar].chats[selectedChat].message.push({
@@ -962,6 +996,11 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
generationId: generationId, generationId: generationId,
} }
}) })
const ind = db.characters[selectedChar].chats[selectedChat].message.length - 1
if(inlayResult.promise){
const p = await inlayResult.promise
db.characters[selectedChar].chats[selectedChat].message[ind].data = p
}
} }
db.characters[selectedChar].reloadKeys += 1 db.characters[selectedChar].reloadKeys += 1
await sayTTS(currentChar, result) await sayTTS(currentChar, result)
@@ -1005,6 +1044,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
} }
} }
if(!currentChar.inlayViewScreen){
if(currentChar.viewScreen === 'emotion' && (!emoChanged) && (abortSignal.aborted === false)){ if(currentChar.viewScreen === 'emotion' && (!emoChanged) && (abortSignal.aborted === false)){
let currentEmotion = currentChar.emotionImages let currentEmotion = currentChar.emotionImages
@@ -1198,10 +1238,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
} }
const ch = await stableDiff(currentChar, msgStr) await stableDiff(currentChar, msgStr)
if(ch){
db.characters[selectedChar].chats[selectedChat].sdData = ch
setDatabase(db)
} }
} }

View File

@@ -0,0 +1,92 @@
import { writeInlayImage } from "../image";
import type { character } from "../storage/database";
import { generateAIImage } from "./stableDiff";
export function runInlayScreen(char:character, data:string):{text:string, promise?:Promise<string>} {
if(char.inlayViewScreen){
if(char.viewScreen === 'emotion'){
return {text: data.replace(/<Emotion="(.+?)">/g, '{{emotion::$1}}')}
}
if(char.viewScreen === 'imggen'){
return {
text: data.replace(/<ImgGen="(.+?)">/g,'[Generating...]'),
promise : (async () => {
const promises:Promise<string|false>[] = [];
const neg = char.newGenData.negative
data.replace(/<ImgGen="(.+?)">/g, (match, p1) => {
const prompt = char.newGenData.prompt.replaceAll('{{slot}}', p1)
promises.push((async () => {
const v = await generateAIImage(prompt, char, neg, 'inlay')
if(!v){
return ''
}
const imgHTML = new Image()
imgHTML.src = v
const inlay = await writeInlayImage(imgHTML)
return inlay
})())
return match
})
const d = await Promise.all(promises)
return data.replace(/<ImgGen="(.+?)">/g, () => {
const result = d.shift()
if(result === false){
return ''
}
return result
})
})()
}
}
}
return {text: data}
}
export function updateInlayScreen(char:character):character {
switch(char.viewScreen){
case 'emotion':
if(char.inlayViewScreen){
char.newGenData = {
prompt: '',
negative: '',
instructions: '',
emotionInstructions: `You must always output the character's emotional image as a command at the end of a conversation. The command must be selected from a given list, and it's better to have variety than to repeat images used in previous chats. Use one image, depending on the character's emotion. See the list below. Form: <Emotion="<image command>"> Example: <Emotion="Agree"> List of commands: {{slot}}`,
}
return char
}
char.newGenData = {
prompt: '',
negative: '',
instructions: '',
emotionInstructions: `You must always output the character's emotional image as a command. The command must be selected from a given list, only output the command, depending on the character's emotion. List of commands: {{slot}}`
}
return char
case 'imggen':
if(char.inlayViewScreen){
char.newGenData = {
prompt: 'best quality, {{slot}}',
negative: 'worse quality',
instructions: 'You must always output the character\'s image as a keyword-formatted prompts that can be used in stable diffusion at the end of a conversation. Use one image, depending on character, place, situation, etc. keyword should be long enough. Form: <ImgGen="<keyword-formatted prompt>">',
emotionInstructions: ''
}
return char
}
char.newGenData = {
prompt: 'best quality, {{slot}}',
negative: 'worse quality',
instructions: 'You must always output the character\'s image as a keyword-formatted prompts that can be used in stable diffusion. only output the that prompt, depending on character, place, situation, etc. keyword should be long enough.',
emotionInstructions: ''
}
return char
default:
char.newGenData = {
prompt: '',
negative: '',
instructions: '',
emotionInstructions: ''
}
return char
}
}

View File

@@ -7,7 +7,6 @@ import { CharEmotion } from "../stores"
import type { OpenAIChat } from "." import type { OpenAIChat } from "."
import { processZip } from "./processzip" import { processZip } from "./processzip"
export async function stableDiff(currentChar:character,prompt:string){ export async function stableDiff(currentChar:character,prompt:string){
const mainPrompt = "assistant is a chat analyzer.\nuser will input a data of situation with key and values before chat, and a chat of a user and character.\nView the status of the chat and change the data.\nif data's key starts with $, it must change it every time.\nif data value is none, it must change it."
let db = get(DataBase) let db = get(DataBase)
if(db.sdProvider === ''){ if(db.sdProvider === ''){
@@ -15,53 +14,14 @@ export async function stableDiff(currentChar:character,prompt:string){
return false return false
} }
let proompt = 'Data:'
let currentSd:[string,string][] = [] const proompt = `Chat:\n${prompt}`
const sdData = currentChar.chats[currentChar.chatPage].sdData
if(sdData){
const das = sdData.split('\n')
for(const data of das){
const splited = data.split(':::')
currentSd.push([splited[0].trim(), splited[1].trim()])
}
}
else{
currentSd = JSON.parse(JSON.stringify(currentChar.sdData))
}
for(const d of currentSd){
let val = d[1].trim()
if(val === ''){
val = 'none'
}
if(!d[0].startsWith('|') || d[0] === 'negative' || d[0] === 'always'){
proompt += `\n${d[0].trim()}: ${val}`
}
}
proompt += `\n\nChat:\n${prompt}`
const promptbody:OpenAIChat[] = [ const promptbody:OpenAIChat[] = [
{ {
role:'system', role:'system',
content: mainPrompt content: currentChar.newGenData.instructions
},
{
role: 'user',
content: `Data:\ncharacter's appearance: red hair, cute, black eyes\ncurrent situation: none\n$character's pose: none\n$character's emotion: none\n\nChat:\nuser: *eats breakfeast* \n I'm ready.\ncharacter: Lemon waits patiently outside your room while you get ready. Once you are dressed and have finished your breakfast, she escorts you to the door.\n"Have a good day at school, Master. Don't forget to study hard and make the most of your time there," Lemon reminds you with a smile as she sees you off.`
},
{
role: 'assistant',
content: "character's appearance: red hair, cute, black eyes\ncurrent situation: waking up in the morning\n$character's pose: standing\n$character's emotion: apologetic"
},
{
role:'system',
content: mainPrompt
}, },
{ {
role: 'user', role: 'user',
@@ -69,7 +29,6 @@ export async function stableDiff(currentChar:character,prompt:string){
}, },
] ]
console.log(proompt)
const rq = await requestChatData({ const rq = await requestChatData({
formated: promptbody, formated: promptbody,
currentChar: currentChar, currentChar: currentChar,
@@ -83,38 +42,20 @@ export async function stableDiff(currentChar:character,prompt:string){
alertError(`${rq.result}`) alertError(`${rq.result}`)
return false return false
} }
else{
const res = rq.result
const das = res.split('\n')
for(const data of das){
const splited = data.split(':')
if(splited.length === 2){
for(let i=0;i<currentSd.length;i++){
if(currentSd[i][0].trim() === splited[0]){
currentSd[i][1] = splited[1].trim()
}
}
}
}
}
let returnSdData = currentSd.map((val) => { const r = rq.result
return val.join(':::')
}).join('\n')
const genPrompt = currentChar.newGenData.prompt.replaceAll('{{slot}}', r)
const neg = currentChar.newGenData.negative
return await generateAIImage(genPrompt, currentChar, neg, '')
}
export async function generateAIImage(genPrompt:string, currentChar:character, neg:string, returnSdData:string){
const db = get(DataBase)
if(db.sdProvider === 'webui'){ if(db.sdProvider === 'webui'){
let prompts:string[] = []
let neg = ''
for(let i=0;i<currentSd.length;i++){
if(currentSd[i][0] !== 'negative'){
prompts.push(currentSd[i][1])
}
else{
neg = currentSd[i][1]
}
}
const uri = new URL(db.webUiUrl) const uri = new URL(db.webUiUrl)
uri.pathname = '/sdapi/v1/txt2img' uri.pathname = '/sdapi/v1/txt2img'
@@ -126,7 +67,7 @@ export async function stableDiff(currentChar:character,prompt:string){
"seed": -1, "seed": -1,
"steps": db.sdSteps, "steps": db.sdSteps,
"cfg_scale": db.sdCFG, "cfg_scale": db.sdCFG,
"prompt": prompts.join(','), "prompt": genPrompt,
"negative_prompt": neg, "negative_prompt": neg,
"sampler_name": db.sdConfig.sampler_name, "sampler_name": db.sdConfig.sampler_name,
"enable_hr": db.sdConfig.enable_hr, "enable_hr": db.sdConfig.enable_hr,
@@ -139,6 +80,16 @@ export async function stableDiff(currentChar:character,prompt:string){
} }
}) })
if(returnSdData === 'inlay'){
if(da.ok){
return `data:image/png;base64,${da.data.images[0]}`
}
else{
alertError(JSON.stringify(da.data))
return ''
}
}
if(da.ok){ if(da.ok){
let charemotions = get(CharEmotion) let charemotions = get(CharEmotion)
const img = `data:image/png;base64,${da.data.images[0]}` const img = `data:image/png;base64,${da.data.images[0]}`
@@ -162,19 +113,6 @@ export async function stableDiff(currentChar:character,prompt:string){
} }
if(db.sdProvider === 'novelai'){ if(db.sdProvider === 'novelai'){
let prompts:string[] = []
let neg = ''
for(let i=0;i<currentSd.length;i++){
if(currentSd[i][0] !== 'negative'){
prompts.push(currentSd[i][1])
}
else{
neg = currentSd[i][1]
}
}
let reqlist= {} let reqlist= {}
if(db.NAII2I){ if(db.NAII2I){
@@ -192,7 +130,7 @@ export async function stableDiff(currentChar:character,prompt:string){
reqlist = { reqlist = {
body: { body: {
"action": "img2img", "action": "img2img",
"input": prompts.join(','), "input": genPrompt,
"model": db.NAIImgModel, "model": db.NAIImgModel,
"parameters": { "parameters": {
"seed": seed, "seed": seed,
@@ -226,7 +164,7 @@ export async function stableDiff(currentChar:character,prompt:string){
}else{ }else{
reqlist = { reqlist = {
body: { body: {
"input": prompts.join(','), "input": genPrompt,
"model": db.NAIImgModel, "model": db.NAIImgModel,
"parameters": { "parameters": {
"width": db.NAIImgConfig.width, "width": db.NAIImgConfig.width,
@@ -249,6 +187,17 @@ export async function stableDiff(currentChar:character,prompt:string){
try { try {
const da = await globalFetch(db.NAIImgUrl, reqlist) const da = await globalFetch(db.NAIImgUrl, reqlist)
if(returnSdData === 'inlay'){
if(da.ok){
const img = await processZip(da.data);
return img
}
else{
alertError(Buffer.from(da.data).toString())
return ''
}
}
if(da.ok){ if(da.ok){
let charemotions = get(CharEmotion) let charemotions = get(CharEmotion)
const img = await processZip(da.data); const img = await processZip(da.data);

View File

@@ -588,6 +588,12 @@ export interface character{
globalLore: loreBook[] globalLore: loreBook[]
chaId: string chaId: string
sdData: [string, string][] sdData: [string, string][]
newGenData?: {
prompt: string,
negative: string,
instructions: string,
emotionInstructions: string,
}
customscript: customscript[] customscript: customscript[]
triggerscript: triggerscript[] triggerscript: triggerscript[]
utilityBot: boolean utilityBot: boolean
@@ -641,6 +647,7 @@ export interface character{
extentions?:{[key:string]:any} extentions?:{[key:string]:any}
largePortrait?:boolean largePortrait?:boolean
lorePlus?:boolean lorePlus?:boolean
inlayViewScreen?:boolean
} }

View File

@@ -49,6 +49,7 @@ function createSimpleCharacter(char:character|groupChat){
chaId: char.chaId, chaId: char.chaId,
additionalAssets: char.additionalAssets, additionalAssets: char.additionalAssets,
virtualscript: char.virtualscript, virtualscript: char.virtualscript,
emotionImages: char.emotionImages,
} }
return simpleChar return simpleChar
@@ -71,7 +72,9 @@ function updateCurrentCharacter(){
if(isEqual(gotCharacter, currentChar)){ if(isEqual(gotCharacter, currentChar)){
return return
} }
if((currentChar?.viewScreen === 'vn') !== get(ShowVN)){
ShowVN.set(currentChar?.viewScreen === 'vn') ShowVN.set(currentChar?.viewScreen === 'vn')
}
console.log("Character updated") console.log("Character updated")
CurrentCharacter.set(cloneDeep(currentChar)) CurrentCharacter.set(cloneDeep(currentChar))