Risuai 0.6.3 first commit
This commit is contained in:
474
src/ts/process/index.ts
Normal file
474
src/ts/process/index.ts
Normal file
@@ -0,0 +1,474 @@
|
||||
import { get, writable } from "svelte/store";
|
||||
import { DataBase, setDatabase, type character } from "../database";
|
||||
import { CharEmotion, selectedCharID } from "../stores";
|
||||
import { tokenize, tokenizeNum } from "../tokenizer";
|
||||
import { language } from "../../lang";
|
||||
import { alertError } from "../alert";
|
||||
import { loadLoreBookPrompt } from "../lorebook";
|
||||
import { findCharacterbyId, replacePlaceholders } from "../util";
|
||||
import { requestChatData } from "./request";
|
||||
import { stableDiff } from "./stableDiff";
|
||||
import { processScript } from "./scripts";
|
||||
|
||||
export interface OpenAIChat{
|
||||
role: 'system'|'user'|'assistant'
|
||||
content: string
|
||||
}
|
||||
|
||||
export const doingChat = writable(false)
|
||||
|
||||
export async function sendChat(chatProcessIndex = -1):Promise<boolean> {
|
||||
|
||||
let findCharCache:{[key:string]:character} = {}
|
||||
function findCharacterbyIdwithCache(id:string){
|
||||
const d = findCharCache[id]
|
||||
if(!!d){
|
||||
return d
|
||||
}
|
||||
else{
|
||||
const r = findCharacterbyId(id)
|
||||
findCharCache[id] = r
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
function reformatContent(data:string){
|
||||
return data.trim().replace(`${currentChar.name}:`, '').trim()
|
||||
}
|
||||
|
||||
let isDoing = get(doingChat)
|
||||
|
||||
if(isDoing){
|
||||
if(chatProcessIndex === -1){
|
||||
return false
|
||||
}
|
||||
}
|
||||
doingChat.set(true)
|
||||
|
||||
let db = get(DataBase)
|
||||
let selectedChar = get(selectedCharID)
|
||||
const nowChatroom = db.characters[selectedChar]
|
||||
let currentChar:character
|
||||
|
||||
if(nowChatroom.type === 'group'){
|
||||
if(chatProcessIndex === -1){
|
||||
for(let i=0;i<nowChatroom.characters.length;i++){
|
||||
const r = await sendChat(i)
|
||||
if(!r){
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
else{
|
||||
currentChar = findCharacterbyIdwithCache(nowChatroom.characters[chatProcessIndex])
|
||||
if(!currentChar){
|
||||
alertError(`cannot find character: ${nowChatroom.characters[chatProcessIndex]}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
currentChar = nowChatroom
|
||||
}
|
||||
|
||||
let selectedChat = nowChatroom.chatPage
|
||||
let currentChat = nowChatroom.chats[selectedChat]
|
||||
let maxContextTokens = db.maxContext
|
||||
|
||||
if(db.aiModel === 'gpt35'){
|
||||
if(maxContextTokens > 4000){
|
||||
maxContextTokens = 4000
|
||||
}
|
||||
}
|
||||
if(db.aiModel === 'gpt4'){
|
||||
if(maxContextTokens > 8000){
|
||||
maxContextTokens = 8000
|
||||
}
|
||||
}
|
||||
|
||||
let unformated = {
|
||||
'main':([] as OpenAIChat[]),
|
||||
'jailbreak':([] as OpenAIChat[]),
|
||||
'chats':([] as OpenAIChat[]),
|
||||
'lorebook':([] as OpenAIChat[]),
|
||||
'globalNote':([] as OpenAIChat[]),
|
||||
'authorNote':([] as OpenAIChat[]),
|
||||
'lastChat':([] as OpenAIChat[]),
|
||||
'description':([] as OpenAIChat[]),
|
||||
}
|
||||
|
||||
if(!currentChar.utilityBot){
|
||||
unformated.main.push({
|
||||
role: 'system',
|
||||
content: replacePlaceholders(db.mainPrompt + ((db.additionalPrompt === '' || (!db.promptPreprocess)) ? '' : `\n${db.additionalPrompt}`), currentChar.name)
|
||||
})
|
||||
|
||||
if(db.jailbreakToggle){
|
||||
unformated.jailbreak.push({
|
||||
role: 'system',
|
||||
content: replacePlaceholders(db.jailbreak, currentChar.name)
|
||||
})
|
||||
}
|
||||
|
||||
unformated.globalNote.push({
|
||||
role: 'system',
|
||||
content: replacePlaceholders(db.globalNote, currentChar.name)
|
||||
})
|
||||
}
|
||||
|
||||
unformated.authorNote.push({
|
||||
role: 'system',
|
||||
content: replacePlaceholders(currentChat.note, currentChar.name)
|
||||
})
|
||||
|
||||
unformated.description.push({
|
||||
role: 'system',
|
||||
content: replacePlaceholders((db.promptPreprocess ? db.descriptionPrefix: '') + currentChar.desc, currentChar.name)
|
||||
})
|
||||
|
||||
unformated.lorebook.push({
|
||||
role: 'system',
|
||||
content: replacePlaceholders(await loadLoreBookPrompt(), currentChar.name)
|
||||
})
|
||||
|
||||
//await tokenize currernt
|
||||
let currentTokens = (await tokenize(Object.keys(unformated).map((key) => {
|
||||
return (unformated[key] as OpenAIChat[]).map((d) => {
|
||||
return d.content
|
||||
}).join('\n\n')
|
||||
}).join('\n\n')) + db.maxResponse) + 150
|
||||
|
||||
let chats:OpenAIChat[] = []
|
||||
|
||||
if(nowChatroom.type === 'group'){
|
||||
chats.push({
|
||||
role: 'system',
|
||||
content: '[Start a new group chat]'
|
||||
})
|
||||
}
|
||||
else{
|
||||
chats.push({
|
||||
role: 'system',
|
||||
content: '[Start a new chat]'
|
||||
})
|
||||
}
|
||||
|
||||
chats.push({
|
||||
role: 'assistant',
|
||||
content: processScript(currentChar,
|
||||
replacePlaceholders(nowChatroom.firstMessage, currentChar.name),
|
||||
'editprocess')
|
||||
})
|
||||
currentTokens += await tokenize(processScript(currentChar,
|
||||
replacePlaceholders(nowChatroom.firstMessage, currentChar.name),
|
||||
'editprocess'))
|
||||
|
||||
const ms = currentChat.message
|
||||
for(const msg of ms){
|
||||
let formedChat = processScript(currentChar,replacePlaceholders(msg.data, currentChar.name), 'editprocess')
|
||||
if(nowChatroom.type === 'group'){
|
||||
if(msg.saying && msg.role === 'char'){
|
||||
formedChat = `${findCharacterbyIdwithCache(msg.saying).name}: ${formedChat}`
|
||||
|
||||
}
|
||||
else if(msg.role === 'user'){
|
||||
formedChat = `${db.username}: ${formedChat}`
|
||||
}
|
||||
}
|
||||
|
||||
chats.push({
|
||||
role: msg.role === 'user' ? 'user' : 'assistant',
|
||||
content: formedChat
|
||||
})
|
||||
currentTokens += (await tokenize(formedChat) + 1)
|
||||
}
|
||||
|
||||
if(nowChatroom.type === 'group'){
|
||||
const systemMsg = `[Write the next reply only as ${currentChar.name}]`
|
||||
chats.push({
|
||||
role: 'system',
|
||||
content: systemMsg
|
||||
})
|
||||
currentTokens += (await tokenize(systemMsg) + 1)
|
||||
}
|
||||
|
||||
console.log(currentTokens)
|
||||
console.log(maxContextTokens)
|
||||
|
||||
while(currentTokens > maxContextTokens){
|
||||
if(chats.length <= 1){
|
||||
alertError(language.errors.toomuchtoken)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
currentTokens -= (await tokenize(chats[0].content) + 1)
|
||||
chats.splice(0, 1)
|
||||
}
|
||||
|
||||
console.log(currentTokens)
|
||||
|
||||
let bias:{[key:number]:number} = {}
|
||||
|
||||
for(let i=0;i<currentChar.bias.length;i++){
|
||||
const bia = currentChar.bias[i]
|
||||
const tokens = await tokenizeNum(bia[0])
|
||||
|
||||
for(const token of tokens){
|
||||
bias[token] = bia[1]
|
||||
}
|
||||
}
|
||||
|
||||
for(let i=0;i<db.bias.length;i++){
|
||||
const bia = db.bias[i]
|
||||
const tokens = await tokenizeNum(bia[0])
|
||||
|
||||
for(const token of tokens){
|
||||
bias[token] = bia[1]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unformated.lastChat.push(chats[chats.length - 1])
|
||||
chats.splice(chats.length - 1, 1)
|
||||
|
||||
unformated.chats = chats
|
||||
|
||||
//make into one
|
||||
|
||||
let formated:OpenAIChat[] = []
|
||||
const formatOrder = db.formatingOrder
|
||||
let sysPrompts:string[] = []
|
||||
for(let i=0;i<formatOrder.length;i++){
|
||||
const cha = unformated[formatOrder[i]]
|
||||
if(cha.length === 1 && cha[0].role === 'system'){
|
||||
sysPrompts.push(cha[0].content)
|
||||
}
|
||||
else if(sysPrompts.length > 0){
|
||||
const prompt = sysPrompts.join('\n')
|
||||
|
||||
if(prompt.replace(/\n/g,'').length > 3){
|
||||
formated.push({
|
||||
role: 'system',
|
||||
content: prompt
|
||||
})
|
||||
}
|
||||
sysPrompts = []
|
||||
formated = formated.concat(cha)
|
||||
}
|
||||
else{
|
||||
formated = formated.concat(cha)
|
||||
}
|
||||
}
|
||||
|
||||
if(sysPrompts.length > 0){
|
||||
const prompt = sysPrompts.join('\n')
|
||||
|
||||
if(prompt.replace(/\n/g,'').length > 3){
|
||||
formated.push({
|
||||
role: 'system',
|
||||
content: prompt
|
||||
})
|
||||
}
|
||||
sysPrompts = []
|
||||
}
|
||||
|
||||
|
||||
const req = await requestChatData({
|
||||
formated: formated,
|
||||
bias: bias,
|
||||
currentChar: currentChar
|
||||
}, 'model')
|
||||
|
||||
let result = ''
|
||||
|
||||
if(req.type === 'fail'){
|
||||
alertError(req.result)
|
||||
return false
|
||||
}
|
||||
else{
|
||||
result = reformatContent(req.result)
|
||||
db.characters[selectedChar].chats[selectedChat].message.push({
|
||||
role: 'char',
|
||||
data: result,
|
||||
saying: processScript(currentChar,currentChar.chaId, 'editoutput')
|
||||
})
|
||||
setDatabase(db)
|
||||
}
|
||||
|
||||
|
||||
if(currentChar.viewScreen === 'emotion'){
|
||||
|
||||
let currentEmotion = currentChar.emotionImages
|
||||
|
||||
function shuffleArray(array:string[]) {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
let emotionList = currentEmotion.map((a) => {
|
||||
return a[0]
|
||||
})
|
||||
|
||||
let charemotions = get(CharEmotion)
|
||||
|
||||
let tempEmotion = charemotions[currentChar.chaId]
|
||||
if(!tempEmotion){
|
||||
tempEmotion = []
|
||||
}
|
||||
if(tempEmotion.length > 4){
|
||||
tempEmotion.splice(0, 1)
|
||||
}
|
||||
|
||||
let emobias:{[key:number]:number} = {}
|
||||
|
||||
for(const emo of emotionList){
|
||||
const tokens = await tokenizeNum(emo)
|
||||
for(const token of tokens){
|
||||
emobias[token] = 10
|
||||
}
|
||||
}
|
||||
|
||||
for(let i =0;i<tempEmotion.length;i++){
|
||||
const emo = tempEmotion[i]
|
||||
|
||||
const tokens = await tokenizeNum(emo[0])
|
||||
const modifier = 20 - ((tempEmotion.length - (i + 1)) * (20/4))
|
||||
|
||||
for(const token of tokens){
|
||||
emobias[token] -= modifier
|
||||
if(emobias[token] < -100){
|
||||
emobias[token] = -100
|
||||
}
|
||||
}
|
||||
}
|
||||
// const promptbody:OpenAIChat[] = [
|
||||
// {
|
||||
|
||||
// role:'system',
|
||||
// content: `assistant is a emotion extractor. user will input a prompt of a character, and assistant must output the emotion of a character.\n\n must chosen from this list: ${shuffleArray(emotionList).join(', ')} \noutput only one word.`
|
||||
// },
|
||||
// {
|
||||
// role: 'user',
|
||||
// content: `"Good morning, Master! Is there anything I can do for you today?"`
|
||||
// },
|
||||
// {
|
||||
// role: 'assistant',
|
||||
// content: 'happy'
|
||||
// },
|
||||
// {
|
||||
// role: 'user',
|
||||
// content: result
|
||||
// },
|
||||
// ]
|
||||
|
||||
const promptbody:OpenAIChat[] = [
|
||||
{
|
||||
role:'system',
|
||||
content: `${db.emotionPrompt2 || "From the list below, choose a word that best represents a character's outfit description, action, or emotion in their dialogue. Prioritize selecting words related to outfit first, then action, and lastly emotion. Print out the chosen word."}\n\n list: ${shuffleArray(emotionList).join(', ')} \noutput only one word.`
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `"Good morning, Master! Is there anything I can do for you today?"`
|
||||
},
|
||||
{
|
||||
role: 'assistant',
|
||||
content: 'happy'
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: result
|
||||
},
|
||||
]
|
||||
|
||||
console.log('requesting chat')
|
||||
const rq = await requestChatData({
|
||||
formated: promptbody,
|
||||
bias: emobias,
|
||||
currentChar: currentChar,
|
||||
temperature: 0.4,
|
||||
maxTokens: 30,
|
||||
}, 'submodel')
|
||||
|
||||
if(rq.type === 'fail'){
|
||||
alertError(rq.result)
|
||||
return true
|
||||
}
|
||||
else{
|
||||
emotionList = currentEmotion.map((a) => {
|
||||
return a[0]
|
||||
})
|
||||
try {
|
||||
const emotion:string = rq.result.replace(/ |\n/g,'').trim().toLocaleLowerCase()
|
||||
let emotionSelected = false
|
||||
for(const emo of currentEmotion){
|
||||
if(emo[0] === emotion){
|
||||
const emos:[string, string,number] = [emo[0], emo[1], Date.now()]
|
||||
tempEmotion.push(emos)
|
||||
charemotions[currentChar.chaId] = tempEmotion
|
||||
CharEmotion.set(charemotions)
|
||||
emotionSelected = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if(!emotionSelected){
|
||||
for(const emo of currentEmotion){
|
||||
if(emotion.includes(emo[0])){
|
||||
const emos:[string, string,number] = [emo[0], emo[1], Date.now()]
|
||||
tempEmotion.push(emos)
|
||||
charemotions[currentChar.chaId] = tempEmotion
|
||||
CharEmotion.set(charemotions)
|
||||
emotionSelected = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!emotionSelected && emotionList.includes('neutral')){
|
||||
const emo = currentEmotion[emotionList.indexOf('neutral')]
|
||||
const emos:[string, string,number] = [emo[0], emo[1], Date.now()]
|
||||
tempEmotion.push(emos)
|
||||
charemotions[currentChar.chaId] = tempEmotion
|
||||
CharEmotion.set(charemotions)
|
||||
emotionSelected = true
|
||||
}
|
||||
} catch (error) {
|
||||
alertError(language.errors.httpError + `${error}`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
|
||||
}
|
||||
else if(currentChar.viewScreen === 'imggen'){
|
||||
if(chatProcessIndex !== -1){
|
||||
alertError("Stable diffusion in group chat is not supported")
|
||||
}
|
||||
|
||||
const msgs = db.characters[selectedChar].chats[selectedChat].message
|
||||
let msgStr = ''
|
||||
for(let i = (msgs.length - 1);i>=0;i--){
|
||||
console.log(i,msgs.length,msgs[i])
|
||||
if(msgs[i].role === 'char'){
|
||||
msgStr = `character: ${msgs[i].data.replace(/\n/, ' ')} \n` + msgStr
|
||||
}
|
||||
else{
|
||||
msgStr = `user: ${msgs[i].data.replace(/\n/, ' ')} \n` + msgStr
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const ch = await stableDiff(currentChar, msgStr)
|
||||
if(ch){
|
||||
db.characters[selectedChar].chats[selectedChat].sdData = ch
|
||||
setDatabase(db)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
250
src/ts/process/plugins.ts
Normal file
250
src/ts/process/plugins.ts
Normal file
@@ -0,0 +1,250 @@
|
||||
import { get, writable } from "svelte/store";
|
||||
import { language } from "../../lang";
|
||||
import { alertError } from "../alert";
|
||||
import { DataBase } from "../database";
|
||||
import { checkNullish, selectSingleFile, sleep } from "../util";
|
||||
import type { OpenAIChat } from ".";
|
||||
import { globalFetch } from "../globalApi";
|
||||
|
||||
export const customProviderStore = writable([] as string[])
|
||||
|
||||
interface PluginRequest{
|
||||
url: string
|
||||
header?:{[key:string]:string}
|
||||
body: any,
|
||||
res: string
|
||||
}
|
||||
|
||||
interface ProviderPlugin{
|
||||
name:string
|
||||
displayName?:string
|
||||
script:string
|
||||
arguments:{[key:string]:'int'|'string'|string[]}
|
||||
realArg:{[key:string]:number|string}
|
||||
}
|
||||
|
||||
export type RisuPlugin = ProviderPlugin
|
||||
|
||||
export async function importPlugin(){
|
||||
try {
|
||||
let db = get(DataBase)
|
||||
const f = await selectSingleFile(['js'])
|
||||
if(!f){
|
||||
return
|
||||
}
|
||||
const jsFile = Buffer.from(f.data).toString('utf-8').replace(/^\uFEFF/gm, "");
|
||||
const splitedJs = jsFile.split('\n')
|
||||
let name = ''
|
||||
let displayName:string = undefined
|
||||
let arg:{[key:string]:'int'|'string'|string[]} = {}
|
||||
let realArg:{[key:string]:number|string} = {}
|
||||
for(const line of splitedJs){
|
||||
if(line.startsWith('//@risu-name')){
|
||||
const provied = line.slice(13)
|
||||
if(provied === ''){
|
||||
alertError('plugin name must be longer than "", did you put it correctly?')
|
||||
return
|
||||
}
|
||||
name = provied.trim()
|
||||
}
|
||||
if(line.startsWith('//@risu-display-name')){
|
||||
const provied = line.slice('//@risu-display-name'.length + 1)
|
||||
if(provied === ''){
|
||||
alertError('plugin display name must be longer than "", did you put it correctly?')
|
||||
return
|
||||
}
|
||||
name = provied.trim()
|
||||
}
|
||||
if(line.startsWith('//@risu-arg')){
|
||||
const provied = line.trim().split(' ')
|
||||
if(provied.length < 3){
|
||||
alertError('plugin argument is incorrect, did you put space in argument name?')
|
||||
return
|
||||
}
|
||||
const provKey = provied[1]
|
||||
|
||||
if(provied[2] !== 'int' && provied[2] !== 'string'){
|
||||
alertError(`plugin argument type is "${provied[2]}", which is an unknown type.`)
|
||||
return
|
||||
}
|
||||
if(provied[2] === 'int'){
|
||||
arg[provKey] = 'int'
|
||||
realArg[provKey] = 0
|
||||
}
|
||||
else if(provied[2] === 'string'){
|
||||
arg[provKey] = 'string'
|
||||
realArg[provKey] = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(name.length === 0){
|
||||
alertError('plugin name not found, did you put it correctly?')
|
||||
return
|
||||
}
|
||||
|
||||
let pluginData:RisuPlugin = {
|
||||
name: name,
|
||||
script: jsFile,
|
||||
realArg: realArg,
|
||||
arguments: arg,
|
||||
displayName: displayName
|
||||
}
|
||||
|
||||
db.plugins.push(pluginData)
|
||||
|
||||
DataBase.set(db)
|
||||
loadPlugins()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
alertError(language.errors.noData)
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentPluginMax(prov:string){
|
||||
return 12000
|
||||
}
|
||||
|
||||
let pluginWorker:Worker = null
|
||||
let providerRes:{success:boolean, content:string} = null
|
||||
|
||||
function postMsgPluginWorker(type:string, body:any){
|
||||
const bod = {
|
||||
type: type,
|
||||
body: body
|
||||
}
|
||||
pluginWorker.postMessage(bod)
|
||||
}
|
||||
|
||||
export async function loadPlugins() {
|
||||
let db = get(DataBase)
|
||||
if(pluginWorker){
|
||||
pluginWorker.terminate()
|
||||
pluginWorker = null
|
||||
}
|
||||
if(db.plugins.length > 0){
|
||||
|
||||
const da = await fetch("/pluginApi.js")
|
||||
const pluginApiString = await da.text()
|
||||
let pluginjs = `${pluginApiString}\n`
|
||||
|
||||
for(const plug of db.plugins){
|
||||
pluginjs += `(() => {${plug.script}})()`
|
||||
}
|
||||
|
||||
const blob = new Blob([pluginjs], {type: 'application/javascript'});
|
||||
pluginWorker = new Worker(URL.createObjectURL(blob));
|
||||
|
||||
pluginWorker.addEventListener('message', async (msg) => {
|
||||
const data:{type:string,body:any} = msg.data
|
||||
switch(data.type){
|
||||
case "addProvider":{
|
||||
let provs = get(customProviderStore)
|
||||
provs.push(data.body)
|
||||
customProviderStore.set(provs)
|
||||
console.log(provs)
|
||||
break
|
||||
}
|
||||
case "resProvider":{
|
||||
const provres:{success:boolean, content:string} = data.body
|
||||
if(checkNullish(provres.success) || checkNullish(provres.content)){
|
||||
providerRes = {
|
||||
success: false,
|
||||
content :"provider didn't respond 'success' or 'content' in response object"
|
||||
}
|
||||
}
|
||||
else if(typeof(provres.content) !== 'string'){
|
||||
providerRes = {
|
||||
success: false,
|
||||
content :"provider didn't respond 'content' in response object in string"
|
||||
}
|
||||
}
|
||||
else{
|
||||
providerRes = {
|
||||
success: !!provres.success,
|
||||
content: provres.content
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case "fetch": {
|
||||
postMsgPluginWorker('fetchData',{
|
||||
id: data.body.id,
|
||||
data: await globalFetch(data.body.url, data.body.arg)
|
||||
})
|
||||
break
|
||||
}
|
||||
case "getArg":{
|
||||
try {
|
||||
const db = get(DataBase)
|
||||
const arg:string[] = data.body.arg.split('::')
|
||||
for(const plug of db.plugins){
|
||||
if(arg[0] === plug.name){
|
||||
postMsgPluginWorker('fetchData',{
|
||||
id: data.body.id,
|
||||
data: plug.realArg[arg[1]]
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
postMsgPluginWorker('fetchData',{
|
||||
id: data.body.id,
|
||||
data: null
|
||||
})
|
||||
} catch (error) {
|
||||
postMsgPluginWorker('fetchData',{
|
||||
id: data.body.id,
|
||||
data: null
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
case "log":{
|
||||
console.log(data.body)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function pluginProcess(arg:{
|
||||
prompt_chat: OpenAIChat,
|
||||
temperature: number,
|
||||
max_tokens: number,
|
||||
presence_penalty: number
|
||||
frequency_penalty: number
|
||||
bias: {[key:string]:string}
|
||||
}|{}){
|
||||
try {
|
||||
let db = get(DataBase)
|
||||
if(!pluginWorker){
|
||||
return {
|
||||
success: false,
|
||||
content: "plugin worker not found error"
|
||||
}
|
||||
}
|
||||
postMsgPluginWorker("requestProvider", {
|
||||
key: db.currentPluginProvider,
|
||||
arg: arg
|
||||
})
|
||||
providerRes = null
|
||||
while(true){
|
||||
await sleep(50)
|
||||
if(providerRes){
|
||||
break
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: providerRes.success,
|
||||
content: providerRes.content
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
content: "unknownError"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
215
src/ts/process/request.ts
Normal file
215
src/ts/process/request.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import { get } from "svelte/store";
|
||||
import type { OpenAIChat } from ".";
|
||||
import { DataBase, setDatabase, type character } from "../database";
|
||||
import { pluginProcess } from "./plugins";
|
||||
import { language } from "../../lang";
|
||||
import { stringlizeChat } from "./stringlize";
|
||||
import { globalFetch } from "../globalApi";
|
||||
|
||||
interface requestDataArgument{
|
||||
formated: OpenAIChat[]
|
||||
bias: {[key:number]:number}
|
||||
currentChar: character
|
||||
temperature?: number
|
||||
maxTokens?:number
|
||||
PresensePenalty?: number
|
||||
frequencyPenalty?: number
|
||||
}
|
||||
|
||||
type requestDataResponse = {
|
||||
type: 'success'|'fail'
|
||||
result: string
|
||||
}
|
||||
|
||||
export async function requestChatData(arg:requestDataArgument, model:'model'|'submodel'):Promise<requestDataResponse> {
|
||||
const db = get(DataBase)
|
||||
let trys = 0
|
||||
while(true){
|
||||
const da = await requestChatDataMain(arg, model)
|
||||
if(da.type === 'success'){
|
||||
return da
|
||||
}
|
||||
trys += 1
|
||||
if(trys > db.requestRetrys){
|
||||
return da
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function requestChatDataMain(arg:requestDataArgument, model:'model'|'submodel'):Promise<requestDataResponse> {
|
||||
const db = get(DataBase)
|
||||
let result = ''
|
||||
let formated = arg.formated
|
||||
let maxTokens = db.maxResponse
|
||||
let bias = arg.bias
|
||||
let currentChar = arg.currentChar
|
||||
const replacer = model === 'model' ? db.forceReplaceUrl : db.forceReplaceUrl2
|
||||
const aiModel = model === 'model' ? db.aiModel : db.subModel
|
||||
|
||||
switch(aiModel){
|
||||
case 'gpt35':
|
||||
case 'gpt4':{
|
||||
const body = ({
|
||||
model: aiModel === 'gpt35' ? 'gpt-3.5-turbo' : 'gpt-4',
|
||||
messages: formated,
|
||||
temperature: arg.temperature ?? (db.temperature / 100),
|
||||
max_tokens: arg.maxTokens ?? maxTokens,
|
||||
presence_penalty: arg.PresensePenalty ?? (db.PresensePenalty / 100),
|
||||
frequency_penalty: arg.frequencyPenalty ?? (db.frequencyPenalty / 100),
|
||||
logit_bias: bias,
|
||||
})
|
||||
|
||||
let replacerURL = replacer === '' ? 'https://api.openai.com/v1/chat/completions' : replacer
|
||||
|
||||
if(replacerURL.endsWith('v1')){
|
||||
replacerURL += '/chat/completions'
|
||||
}
|
||||
if(replacerURL.endsWith('v1/')){
|
||||
replacerURL += 'chat/completions'
|
||||
}
|
||||
|
||||
const res = await globalFetch(replacerURL, {
|
||||
body: body,
|
||||
headers: {
|
||||
"Authorization": "Bearer " + db.openAIKey
|
||||
},
|
||||
})
|
||||
|
||||
const dat = res.data as any
|
||||
if(res.ok){
|
||||
try {
|
||||
const msg:OpenAIChat = (dat.choices[0].message)
|
||||
return {
|
||||
type: 'success',
|
||||
result: msg.content
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
type: 'fail',
|
||||
result: (language.errors.httpError + `${JSON.stringify(dat)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(dat.error && dat.error.message){
|
||||
return {
|
||||
type: 'fail',
|
||||
result: (language.errors.httpError + `${dat.error.message}`)
|
||||
}
|
||||
}
|
||||
else{
|
||||
return {
|
||||
type: 'fail',
|
||||
result: (language.errors.httpError + `${JSON.stringify(res.data)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
case "textgen_webui":{
|
||||
let DURL = db.textgenWebUIURL
|
||||
if((!DURL.endsWith('textgen')) && (!DURL.endsWith('textgen/'))){
|
||||
if(DURL.endsWith('/')){
|
||||
DURL += 'run/textgen'
|
||||
}
|
||||
else{
|
||||
DURL += '/run/textgen'
|
||||
}
|
||||
}
|
||||
|
||||
const proompt = stringlizeChat(formated, currentChar.name)
|
||||
|
||||
const payload = [
|
||||
proompt,
|
||||
{
|
||||
'max_new_tokens': 80,
|
||||
'do_sample': true,
|
||||
'temperature': (db.temperature / 100),
|
||||
'top_p': 0.9,
|
||||
'typical_p': 1,
|
||||
'repetition_penalty': (db.PresensePenalty / 100),
|
||||
'encoder_repetition_penalty': 1,
|
||||
'top_k': 100,
|
||||
'min_length': 0,
|
||||
'no_repeat_ngram_size': 0,
|
||||
'num_beams': 1,
|
||||
'penalty_alpha': 0,
|
||||
'length_penalty': 1,
|
||||
'early_stopping': false,
|
||||
'truncation_length': maxTokens,
|
||||
'ban_eos_token': false,
|
||||
'custom_stopping_strings': [`\nUser:`],
|
||||
'seed': -1,
|
||||
add_bos_token: true,
|
||||
}
|
||||
];
|
||||
|
||||
const bodyTemplate = { "data": [JSON.stringify(payload)] };
|
||||
|
||||
const res = await globalFetch(DURL, {
|
||||
body: bodyTemplate,
|
||||
headers: {}
|
||||
})
|
||||
|
||||
const dat = res.data as any
|
||||
console.log(DURL)
|
||||
console.log(res.data)
|
||||
if(res.ok){
|
||||
try {
|
||||
return {
|
||||
type: 'success',
|
||||
result: dat.data[0].substring(proompt.length)
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
type: 'fail',
|
||||
result: (language.errors.httpError + `${error}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
return {
|
||||
type: 'fail',
|
||||
result: (language.errors.httpError + `${JSON.stringify(res.data)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'custom':{
|
||||
const d = await pluginProcess({
|
||||
bias: bias,
|
||||
prompt_chat: formated,
|
||||
temperature: (db.temperature / 100),
|
||||
max_tokens: maxTokens,
|
||||
presence_penalty: (db.PresensePenalty / 100),
|
||||
frequency_penalty: (db.frequencyPenalty / 100)
|
||||
})
|
||||
if(!d){
|
||||
return {
|
||||
type: 'fail',
|
||||
result: (language.errors.unknownModel)
|
||||
}
|
||||
}
|
||||
else if(!d.success){
|
||||
return {
|
||||
type: 'fail',
|
||||
result: d.content
|
||||
}
|
||||
}
|
||||
else{
|
||||
return {
|
||||
type: 'success',
|
||||
result: d.content
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
default:{
|
||||
return {
|
||||
type: 'fail',
|
||||
result: (language.errors.unknownModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/ts/process/scripts.ts
Normal file
15
src/ts/process/scripts.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { character } from "../database";
|
||||
|
||||
const dreg = /{{data}}/g
|
||||
|
||||
export function processScript(char:character, data:string, mode:'editinput'|'editoutput'|'editprocess'){
|
||||
for (const script of char.customscript){
|
||||
if(script.type === mode){
|
||||
const reg = new RegExp(script.in,'g')
|
||||
data = data.replace(reg, (v) => {
|
||||
return script.out.replace(dreg, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
158
src/ts/process/stableDiff.ts
Normal file
158
src/ts/process/stableDiff.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { get } from "svelte/store"
|
||||
import { DataBase, type character } from "../database"
|
||||
import { requestChatData } from "./request"
|
||||
import { alertError } from "../alert"
|
||||
import { globalFetch } from "../globalApi"
|
||||
import { CharEmotion } from "../stores"
|
||||
|
||||
|
||||
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)
|
||||
|
||||
if(db.sdProvider === ''){
|
||||
alertError("Stable diffusion is not set in settings.")
|
||||
return false
|
||||
}
|
||||
|
||||
let proompt = 'Data:'
|
||||
|
||||
let currentSd:[string,string][] = []
|
||||
|
||||
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[] = [
|
||||
{
|
||||
|
||||
role:'system',
|
||||
content: mainPrompt
|
||||
},
|
||||
{
|
||||
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',
|
||||
content: proompt
|
||||
},
|
||||
]
|
||||
|
||||
console.log(proompt)
|
||||
const rq = await requestChatData({
|
||||
formated: promptbody,
|
||||
currentChar: currentChar,
|
||||
temperature: 0.2,
|
||||
maxTokens: 300,
|
||||
bias: {}
|
||||
}, 'submodel')
|
||||
|
||||
|
||||
if(rq.type === 'fail'){
|
||||
alertError(rq.result)
|
||||
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) => {
|
||||
return val.join(':::')
|
||||
}).join('\n')
|
||||
|
||||
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)
|
||||
uri.pathname = '/sdapi/v1/txt2img'
|
||||
try {
|
||||
const da = await globalFetch(uri.toString(), {
|
||||
body: {
|
||||
"width": db.sdConfig.width,
|
||||
"height": db.sdConfig.height,
|
||||
"seed": -1,
|
||||
"steps": db.sdSteps,
|
||||
"cfg_scale": db.sdCFG,
|
||||
"prompt": prompts.join(','),
|
||||
"negative_prompt": neg,
|
||||
'sampler_name': db.sdConfig.sampler_name
|
||||
}
|
||||
})
|
||||
|
||||
if(da.ok){
|
||||
let charemotions = get(CharEmotion)
|
||||
const img = `data:image/png;base64,${da.data.images[0]}`
|
||||
console.log(img)
|
||||
const emos:[string, string,number][] = [[img, img, Date.now()]]
|
||||
charemotions[currentChar.chaId] = emos
|
||||
CharEmotion.set(charemotions)
|
||||
}
|
||||
else{
|
||||
alertError(JSON.stringify(da.data))
|
||||
return false
|
||||
}
|
||||
|
||||
return returnSdData
|
||||
|
||||
|
||||
} catch (error) {
|
||||
alertError(error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
21
src/ts/process/stringlize.ts
Normal file
21
src/ts/process/stringlize.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { OpenAIChat } from ".";
|
||||
|
||||
export function multiChatReplacer(){
|
||||
|
||||
}
|
||||
|
||||
export function stringlizeChat(formated:OpenAIChat[], char:string = ''){
|
||||
let resultString:string[] = []
|
||||
for(const form of formated){
|
||||
if(form.role === 'system'){
|
||||
resultString.push("'System Note: " + form.content)
|
||||
}
|
||||
else if(form.role === 'user'){
|
||||
resultString.push("User: " + form.content)
|
||||
}
|
||||
else if(form.role === 'assistant'){
|
||||
resultString.push("Assistant: " + form.content)
|
||||
}
|
||||
}
|
||||
return resultString.join('\n\n') + `\n\n${char}:`
|
||||
}
|
||||
Reference in New Issue
Block a user