Updated to 0.7.0 (#8)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
(() => {
|
||||
interface risuPlugin{
|
||||
providers: {name:string, func:(arg:providerArgument) => Promise<{success:boolean,content:string}>}[]
|
||||
fetchResponseQueue:{id:string,data:any}[]
|
||||
@@ -24,6 +25,24 @@ interface providerArgument{
|
||||
bias?: {[key:string]:string}
|
||||
}
|
||||
|
||||
async function transferDataAsync(type:string,body:any) {
|
||||
const id = `${Date.now()}_${Math.random()}`
|
||||
postMessage({
|
||||
type: 'fetch',
|
||||
body: {id: id, ...body}
|
||||
})
|
||||
while(true){
|
||||
await sleep(50)
|
||||
for(let i=0;i<__risuPlugin__.fetchResponseQueue.length;i++){
|
||||
const q = __risuPlugin__.fetchResponseQueue[i]
|
||||
if(q.id === id){
|
||||
__risuPlugin__.fetchResponseQueue.splice(i, 1)
|
||||
return q.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function risuFetch(url:string, arg:{body:any,headers?:{[key:string]:string}}){
|
||||
const id = `${Date.now()}_${Math.random()}`
|
||||
postMessage({
|
||||
@@ -89,6 +108,18 @@ function printLog(data:any){
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function getChar(){
|
||||
return transferDataAsync('getChar', '')
|
||||
}
|
||||
|
||||
function setChar(char:any){
|
||||
postMessage({
|
||||
type: 'setChar',
|
||||
body: char
|
||||
})
|
||||
}
|
||||
|
||||
async function handleOnmessage(data:{type:string,body:any}) {
|
||||
if(!data.type){
|
||||
return
|
||||
@@ -141,3 +172,10 @@ onmessage = (ev) => {
|
||||
handleOnmessage(ev.data)
|
||||
const data:{type:string,body:any} = ev.data
|
||||
}
|
||||
|
||||
{
|
||||
const __risuPlugin__ = null
|
||||
const transferDataAsync = null
|
||||
//{{placeholder}}
|
||||
}
|
||||
})()
|
||||
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "RisuAI",
|
||||
"version": "0.6.7"
|
||||
"version": "0.7.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
||||
@@ -50,6 +50,13 @@ export const languageEnglish = {
|
||||
+ "\n- if the key starts with **|**, the key's value will not change."
|
||||
+ "\n- if the key starts with **$**, the key's value will more likely to change."
|
||||
+ "\n\nwhen the image is first generated, you can only change it by modifying 'Current Image Generation Data' in below.",
|
||||
|
||||
regexScript: "Regex Script is a custom script that is embbedded to the character. it replaces string that matches IN to OUT.\n\nThere are three type options."
|
||||
+ "- **Modify Input** modifys user's input"
|
||||
+ "- **Modify Output** modifys character's output"
|
||||
+ "- **Modify Request Data** modifys current chat data when sent.\n\nIN must be a regex without flags and *\\*.\n\nOUT is a normal string."
|
||||
+ "\n\n If OUT starts with @@, it doesn't replaces the string, but instead does a special effect if matching string founds."
|
||||
+ "\n\n- @@emo (emotion name)\n\n if character is Emotion Images mode, sets (emotion name) as emotion and prevents default.",
|
||||
experimental: "This is a experimental setting. it might be unstable."
|
||||
},
|
||||
setup: {
|
||||
@@ -199,6 +206,8 @@ export const languageEnglish = {
|
||||
type: "Type",
|
||||
editInput: "Modfiy Input",
|
||||
editOutput: "Modfiy Output",
|
||||
editProcess: "Modfiy Request Data"
|
||||
editProcess: "Modfiy Request Data",
|
||||
loadLatest: "Load Latest Backup",
|
||||
loadOthers: "Load Other Backups"
|
||||
|
||||
}
|
||||
|
||||
@@ -179,7 +179,13 @@ export const languageKorean = {
|
||||
+ "\n- 키의 이름이 **|** 로 시작할 시, 값은 고정됩니다."
|
||||
+ "\n- 키의 이름이 **$** 로 시작할 시, 값은 더 자주 변합니다."
|
||||
+ "\n\n이미지가 처음 생성된 이후부터는 '현재 이미지 생성 데이터'를 수정하여 변경할 수 있습니다.",
|
||||
experimental: "실험적 기능입니다. 불안정할 수 있습니다."
|
||||
experimental: "실험적 기능입니다. 불안정할 수 있습니다.",
|
||||
regexScript: "정규식 스크립트는 캐릭터에 종속된 커스텀 스크립트입니다. IN의 조건에 맞는 문자열을 OUT으로 변경합니다.\n\n타입은 세가지가 있습니다."
|
||||
+ "- **입력문 수정** 유저의 입력문을 수정합니다"
|
||||
+ "- **출력문 수정** 캐릭터의 출력문을 수정합니다"
|
||||
+ "- **리퀘스트 데이터 수정** 리퀘스트를 보낼 때 채팅 데이터를 수정합니다.\n\nIN은 flag와 *\\* 가 없는 Regex여야 합니다.\n\nOUT은 일반 문자열입니다."
|
||||
+ "\n\n 만약 OUT이 @@로 시작된다면, 특수한 효과를 냅니다"
|
||||
+ "\n\n- @@emo (emotion name)\n\n 감정 이미지 모드일 시 (emotion name)을 감정으로 정하고 감정 처리를 하지 않습니다.",
|
||||
},
|
||||
setup: {
|
||||
chooseProvider: "AI 제공자를 선택해 주세요",
|
||||
@@ -199,5 +205,7 @@ export const languageKorean = {
|
||||
type: "타입",
|
||||
editInput: "입력문 수정",
|
||||
editOutput: "출력문 수정",
|
||||
editProcess: "리퀘스트 데이터 수정"
|
||||
editProcess: "리퀘스트 데이터 수정",
|
||||
loadLatest: "가장 최근 백업 불러오기",
|
||||
loadOthers: "다른 백업 불러오기"
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
import sendSound from '../../etc/send.mp3'
|
||||
import {cloneDeep} from 'lodash'
|
||||
import { processScript } from "src/ts/process/scripts";
|
||||
import GithubStars from "../Others/GithubStars.svelte";
|
||||
|
||||
let messageInput = ''
|
||||
let openMenu = false
|
||||
@@ -186,6 +187,7 @@
|
||||
<div class="h-full w-full flex flex-col overflow-y-auto items-center">
|
||||
<h2 class="text-4xl text-white mb-0 mt-6 font-black">RisuAI</h2>
|
||||
<h3 class="text-gray-500 mt-1">Version {appVer}</h3>
|
||||
<GithubStars />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="h-full w-full flex flex-col-reverse overflow-y-auto relative" on:scroll={(e) => {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
{#if $alertStore.type !== 'none' && $alertStore.type !== 'toast'}
|
||||
<div class="absolute w-full h-full z-50 bg-black bg-opacity-50 flex justify-center items-center" class:vis={ $alertStore.type === 'wait2'}>
|
||||
<div class="bg-darkbg p-4 break-any rounded-md flex flex-col max-w-3xl max-h-11/12 overflow-y-auto">
|
||||
<div class="bg-darkbg p-4 break-any rounded-md flex flex-col max-w-3xl max-h-full overflow-y-auto">
|
||||
{#if $alertStore.type === 'error'}
|
||||
<h2 class="text-red-700 mt-0 mb-2 w-40 max-w-full">Error</h2>
|
||||
{:else if $alertStore.type === 'ask'}
|
||||
|
||||
11
src/lib/Others/GithubStars.svelte
Normal file
11
src/lib/Others/GithubStars.svelte
Normal file
@@ -0,0 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { isTauri } from "src/ts/globalApi";
|
||||
|
||||
</script>
|
||||
<svelte:head>
|
||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||
</svelte:head>
|
||||
<!-- Place this tag where you want the button to render. -->
|
||||
{#if !isTauri}
|
||||
<a class="github-button mt-4" href="https://github.com/kwaroran/risuAI" data-color-scheme="no-preference: dark; light: dark; dark: dark;" data-size="large" data-show-count="true" aria-label="Star kwaroran/risuAI on GitHub">Star</a>
|
||||
{/if}
|
||||
@@ -396,7 +396,7 @@
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
<span class="text-neutral-200 mt-4">{language.regexScript} <Help key="experimental"/></span>
|
||||
<span class="text-neutral-200 mt-4">{language.regexScript} <Help key="regexScript"/></span>
|
||||
<table class="contain w-full max-w-full tabler mt-2 flex flex-col p-2 gap-2">
|
||||
{#if currentChar.data.customscript.length === 0}
|
||||
<div class="text-gray-500">No Scripts</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { saveImage as saveImageGlobal } from './globalApi';
|
||||
|
||||
export const DataBase = writable({} as any as Database)
|
||||
export const loadedStore = writable(false)
|
||||
export let appVer = '0.6.7'
|
||||
export let appVer = '0.7.0'
|
||||
|
||||
|
||||
export function setDatabase(data:Database){
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { get } from "svelte/store";
|
||||
import { alertError, alertInput, alertNormal, alertStore } from "../alert";
|
||||
import { alertError, alertInput, alertNormal, alertSelect, alertStore } from "../alert";
|
||||
import { DataBase, setDatabase, type Database } from "../database";
|
||||
import { forageStorage, getUnpargeables, isTauri } from "../globalApi";
|
||||
import pako from "pako";
|
||||
@@ -177,8 +177,8 @@ async function loadDrive(ACCESS_TOKEN:string) {
|
||||
return d.name
|
||||
})
|
||||
|
||||
let latestDb:DriveFile = null
|
||||
let latestDbDate = 0
|
||||
|
||||
let dbs:[DriveFile,number][] = []
|
||||
|
||||
for(const f of files){
|
||||
if(f.name.endsWith("-database.risudat")){
|
||||
@@ -187,15 +187,26 @@ async function loadDrive(ACCESS_TOKEN:string) {
|
||||
continue
|
||||
}
|
||||
else{
|
||||
if(tm > latestDbDate){
|
||||
latestDb = f
|
||||
latestDbDate = tm
|
||||
dbs.push([f,tm])
|
||||
}
|
||||
}
|
||||
}
|
||||
dbs.sort((a,b) => {
|
||||
return b[1] - a[1]
|
||||
})
|
||||
|
||||
if(dbs.length !== 0){
|
||||
let selectables:string[] = []
|
||||
for(let i=0;i<dbs.length;i++){
|
||||
selectables.push(`Backup saved in ${(new Date(dbs[i][1] * 1000)).toLocaleString()}`)
|
||||
if(selectables.length > 7){
|
||||
break
|
||||
}
|
||||
if(latestDbDate !== 0){
|
||||
const db:Database = JSON.parse(Buffer.from(pako.inflate(await getFileData(ACCESS_TOKEN, latestDb.id))).toString('utf-8'))
|
||||
}
|
||||
const selectedIndex = (await alertSelect([language.loadLatest, language.loadOthers]) === '0') ? 0 : parseInt(await alertSelect(selectables))
|
||||
const selectedDb = dbs[selectedIndex][0]
|
||||
|
||||
const db:Database = JSON.parse(Buffer.from(pako.inflate(await getFileData(ACCESS_TOKEN, selectedDb.id))).toString('utf-8'))
|
||||
const requiredImages = (getUnpargeables(db))
|
||||
let ind = 0;
|
||||
for(const images of requiredImages){
|
||||
@@ -253,6 +264,9 @@ async function loadDrive(ACCESS_TOKEN:string) {
|
||||
})
|
||||
}
|
||||
}
|
||||
else{
|
||||
location.search = ''
|
||||
}
|
||||
}
|
||||
|
||||
function checkImageExist(image:string){
|
||||
|
||||
@@ -8,7 +8,7 @@ import { loadLoreBookPrompt } from "../lorebook";
|
||||
import { findCharacterbyId, replacePlaceholders } from "../util";
|
||||
import { requestChatData } from "./request";
|
||||
import { stableDiff } from "./stableDiff";
|
||||
import { processScript } from "./scripts";
|
||||
import { processScript, processScriptFull } from "./scripts";
|
||||
|
||||
export interface OpenAIChat{
|
||||
role: 'system'|'user'|'assistant'
|
||||
@@ -282,23 +282,26 @@ export async function sendChat(chatProcessIndex = -1):Promise<boolean> {
|
||||
}, 'model')
|
||||
|
||||
let result = ''
|
||||
let emoChanged = false
|
||||
|
||||
if(req.type === 'fail'){
|
||||
alertError(req.result)
|
||||
return false
|
||||
}
|
||||
else{
|
||||
result = reformatContent(req.result)
|
||||
const result2 = processScriptFull(currentChar, reformatContent(req.result), 'editoutput')
|
||||
result = result2.data
|
||||
emoChanged = result2.emoChanged
|
||||
db.characters[selectedChar].chats[selectedChat].message.push({
|
||||
role: 'char',
|
||||
data: result,
|
||||
saying: processScript(currentChar,currentChar.chaId, 'editoutput')
|
||||
saying: currentChar.chaId
|
||||
})
|
||||
setDatabase(db)
|
||||
}
|
||||
|
||||
|
||||
if(currentChar.viewScreen === 'emotion'){
|
||||
if(currentChar.viewScreen === 'emotion' && (!emoChanged)){
|
||||
|
||||
let currentEmotion = currentChar.emotionImages
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { DataBase } from "../database";
|
||||
import { checkNullish, selectSingleFile, sleep } from "../util";
|
||||
import type { OpenAIChat } from ".";
|
||||
import { globalFetch } from "../globalApi";
|
||||
import { selectedCharID } from "../stores";
|
||||
|
||||
export const customProviderStore = writable([] as string[])
|
||||
|
||||
@@ -107,6 +108,7 @@ export function getCurrentPluginMax(prov:string){
|
||||
|
||||
let pluginWorker:Worker = null
|
||||
let providerRes:{success:boolean, content:string} = null
|
||||
let translatorRes:{success:boolean, content:string} = null
|
||||
|
||||
function postMsgPluginWorker(type:string, body:any){
|
||||
const bod = {
|
||||
@@ -116,6 +118,8 @@ function postMsgPluginWorker(type:string, body:any){
|
||||
pluginWorker.postMessage(bod)
|
||||
}
|
||||
|
||||
let pluginTranslator = false
|
||||
|
||||
export async function loadPlugins() {
|
||||
let db = get(DataBase)
|
||||
if(pluginWorker){
|
||||
@@ -127,10 +131,12 @@ export async function loadPlugins() {
|
||||
const da = await fetch("/pluginApi.js")
|
||||
const pluginApiString = await da.text()
|
||||
let pluginjs = `${pluginApiString}\n`
|
||||
let pluginLoadedJs = ''
|
||||
|
||||
for(const plug of db.plugins){
|
||||
pluginjs += `(() => {${plug.script}})()`
|
||||
pluginLoadedJs += `(() => {${plug.script}})()`
|
||||
}
|
||||
pluginjs = pluginjs.replace('//{{placeholder}}',pluginLoadedJs)
|
||||
|
||||
const blob = new Blob([pluginjs], {type: 'application/javascript'});
|
||||
pluginWorker = new Worker(URL.createObjectURL(blob));
|
||||
@@ -167,6 +173,31 @@ export async function loadPlugins() {
|
||||
}
|
||||
break
|
||||
}
|
||||
case "resTrans":{
|
||||
const provres:{success:boolean, content:string} = data.body
|
||||
if(checkNullish(provres.success) || checkNullish(provres.content)){
|
||||
translatorRes = {
|
||||
success: false,
|
||||
content :"plugin didn't respond 'success' or 'content' in response object"
|
||||
}
|
||||
}
|
||||
else if(typeof(provres.content) !== 'string'){
|
||||
translatorRes = {
|
||||
success: false,
|
||||
content :"plugin didn't respond 'content' in response object in string"
|
||||
}
|
||||
}
|
||||
else{
|
||||
translatorRes = {
|
||||
success: !!provres.success,
|
||||
content: provres.content
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case "useTranslator": {
|
||||
pluginTranslator = true
|
||||
}
|
||||
case "fetch": {
|
||||
postMsgPluginWorker('fetchData',{
|
||||
id: data.body.id,
|
||||
@@ -199,6 +230,20 @@ export async function loadPlugins() {
|
||||
}
|
||||
break
|
||||
}
|
||||
case "getChar":{
|
||||
const db = get(DataBase)
|
||||
const charid = get(selectedCharID)
|
||||
const char = db.characters[charid]
|
||||
postMsgPluginWorker('fetchData',{
|
||||
id: data.body.id,
|
||||
data: char
|
||||
})
|
||||
}
|
||||
case "setChar":{
|
||||
const db = get(DataBase)
|
||||
const charid = get(selectedCharID)
|
||||
db.characters[charid] = data.body
|
||||
}
|
||||
case "log":{
|
||||
console.log(data.body)
|
||||
break
|
||||
@@ -208,6 +253,33 @@ export async function loadPlugins() {
|
||||
}
|
||||
}
|
||||
|
||||
export async function translatorPlugin(text:string, from:string, to:string) {
|
||||
if(!pluginTranslator){
|
||||
return false
|
||||
}
|
||||
else{
|
||||
try {
|
||||
translatorRes = null
|
||||
postMsgPluginWorker("requestTrans", {text, from, to})
|
||||
while(true){
|
||||
await sleep(50)
|
||||
if(providerRes){
|
||||
break
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: translatorRes.success,
|
||||
content: translatorRes.content
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
content: "unknownError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function pluginProcess(arg:{
|
||||
prompt_chat: OpenAIChat,
|
||||
temperature: number,
|
||||
|
||||
@@ -1,15 +1,52 @@
|
||||
import { get } from "svelte/store";
|
||||
import { CharEmotion, selectedCharID } from "../stores";
|
||||
import type { character } from "../database";
|
||||
|
||||
const dreg = /{{data}}/g
|
||||
|
||||
export function processScript(char:character, data:string, mode:'editinput'|'editoutput'|'editprocess'){
|
||||
type ScriptMode = 'editinput'|'editoutput'|'editprocess'
|
||||
|
||||
export function processScript(char:character, data:string, mode:ScriptMode){
|
||||
return processScriptFull(char, data, mode).data
|
||||
}
|
||||
|
||||
export function processScriptFull(char:character, data:string, mode:ScriptMode){
|
||||
let emoChanged = false
|
||||
for (const script of char.customscript){
|
||||
if(script.type === mode){
|
||||
const reg = new RegExp(script.in,'g')
|
||||
data = data.replace(reg, (v) => {
|
||||
if(script.out.startsWith('@@emo ')){
|
||||
if(char.viewScreen !== 'emotion'){
|
||||
return v
|
||||
}
|
||||
if(emoChanged){
|
||||
return v
|
||||
}
|
||||
const emoName = script.out.substring(6).trim()
|
||||
let charemotions = get(CharEmotion)
|
||||
let tempEmotion = charemotions[char.chaId]
|
||||
if(!tempEmotion){
|
||||
tempEmotion = []
|
||||
}
|
||||
if(tempEmotion.length > 4){
|
||||
tempEmotion.splice(0, 1)
|
||||
}
|
||||
for(const emo of char.emotionImages){
|
||||
if(emo[0] === emoName){
|
||||
const emos:[string, string,number] = [emo[0], emo[1], Date.now()]
|
||||
tempEmotion.push(emos)
|
||||
charemotions[char.chaId] = tempEmotion
|
||||
CharEmotion.set(charemotions)
|
||||
emoChanged = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
return script.out.replace(dreg, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
return data
|
||||
return {data, emoChanged}
|
||||
}
|
||||
@@ -1,28 +1,34 @@
|
||||
import { Body,fetch,ResponseType } from "@tauri-apps/api/http"
|
||||
import { isTauri } from "../globalApi"
|
||||
import { translatorPlugin } from "../process/plugins"
|
||||
|
||||
let cache={
|
||||
origin: [''],
|
||||
trans: ['']
|
||||
}
|
||||
|
||||
export async function translate(params:string, reverse:boolean) {
|
||||
export async function translate(text:string, reverse:boolean) {
|
||||
if(!isTauri){
|
||||
return params
|
||||
return text
|
||||
}
|
||||
const plug = await translatorPlugin(text, reverse ? 'ko' : 'en', reverse ? 'en' : 'ko')
|
||||
if(plug){
|
||||
return plug.content
|
||||
}
|
||||
if(!reverse){
|
||||
const ind = cache.origin.indexOf(params)
|
||||
const ind = cache.origin.indexOf(text)
|
||||
if(ind !== -1){
|
||||
return cache.trans[ind]
|
||||
}
|
||||
}
|
||||
else{
|
||||
const ind = cache.trans.indexOf(params)
|
||||
const ind = cache.trans.indexOf(text)
|
||||
if(ind !== -1){
|
||||
return cache.origin[ind]
|
||||
}
|
||||
}
|
||||
return googleTrans(params, reverse)
|
||||
|
||||
return googleTrans(text, reverse)
|
||||
}
|
||||
|
||||
async function googleTrans(text:string, reverse:boolean) {
|
||||
@@ -45,5 +51,8 @@ async function googleTrans(text:string, reverse:boolean) {
|
||||
})
|
||||
|
||||
const res = f.data as {sentences:{trans?:string}[]}
|
||||
if(typeof(f.data) === 'string'){
|
||||
return res as unknown as string
|
||||
}
|
||||
return res.sentences.filter((s) => 'trans' in s).map((s) => s.trans).join('');
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"version":"0.6.7"}
|
||||
{"version":"0.7.0"}
|
||||
|
||||
Reference in New Issue
Block a user