Update to 0.9.0 (#50)
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "RisuAI",
|
"productName": "RisuAI",
|
||||||
"version": "0.8.2"
|
"version": "0.9.0"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
|||||||
@@ -241,5 +241,6 @@ export const languageEnglish = {
|
|||||||
useGlobalSettings: "Use Global Settings",
|
useGlobalSettings: "Use Global Settings",
|
||||||
recursiveScanning: "Recursive Scanning",
|
recursiveScanning: "Recursive Scanning",
|
||||||
creator: "Creator",
|
creator: "Creator",
|
||||||
CharVersion: "Character Version"
|
CharVersion: "Character Version",
|
||||||
|
Speech: "Speech"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,27 @@
|
|||||||
selectedCharID.set(index)
|
selectedCharID.set(index)
|
||||||
endGrid()
|
endGrid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatChars(search:string){
|
||||||
|
let charas:{
|
||||||
|
image:string
|
||||||
|
index:number
|
||||||
|
type:string
|
||||||
|
}[] = []
|
||||||
|
|
||||||
|
for(let i=0;i<$DataBase.characters.length;i++){
|
||||||
|
const c = $DataBase.characters[i]
|
||||||
|
if(c.name.replace(/ /g,"").toLocaleLowerCase().includes(search.toLocaleLowerCase().replace(/ /g,""))){
|
||||||
|
charas.push({
|
||||||
|
image: c.image,
|
||||||
|
index: i,
|
||||||
|
type: c.type
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return charas
|
||||||
|
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="h-full w-full flex justify-center">
|
<div class="h-full w-full flex justify-center">
|
||||||
@@ -20,14 +41,12 @@
|
|||||||
<input class="text-neutral-200 mt-2 mb-4 p-2 bg-transparent input-text focus:bg-selected w-4/5 text-xl" placeholder="Search" bind:value={search}>
|
<input class="text-neutral-200 mt-2 mb-4 p-2 bg-transparent input-text focus:bg-selected w-4/5 text-xl" placeholder="Search" bind:value={search}>
|
||||||
<div class="w-full flex justify-center">
|
<div class="w-full flex justify-center">
|
||||||
<div class="flex flex-wrap gap-2 mx-auto container">
|
<div class="flex flex-wrap gap-2 mx-auto container">
|
||||||
{#each $DataBase.characters.filter((c) => {
|
{#each formatChars(search) as char}
|
||||||
return c.name.toLocaleLowerCase().includes(search.toLocaleLowerCase())
|
|
||||||
}) as char, i}
|
|
||||||
<div class="flex items-center text-neutral-200">
|
<div class="flex items-center text-neutral-200">
|
||||||
{#if char.image}
|
{#if char.image}
|
||||||
<BarIcon onClick={() => {changeChar(i)}} additionalStyle={getCharImage($DataBase.characters[i].image, 'css')}></BarIcon>
|
<BarIcon onClick={() => {changeChar(char.index)}} additionalStyle={getCharImage(char.image, 'css')}></BarIcon>
|
||||||
{:else}
|
{:else}
|
||||||
<BarIcon onClick={() => {changeChar(i)}} additionalStyle={i === $selectedCharID ? 'background:#44475a' : ''}>
|
<BarIcon onClick={() => {changeChar(char.index)}} additionalStyle={char.index === $selectedCharID ? 'background:#44475a' : ''}>
|
||||||
{#if char.type === 'group'}
|
{#if char.type === 'group'}
|
||||||
<Users />
|
<Users />
|
||||||
{:else}
|
{:else}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { tokenize } from "../../ts/tokenizer";
|
import { tokenize } from "../../ts/tokenizer";
|
||||||
import { DataBase, type Database, type character, type groupChat } from "../../ts/database";
|
import { DataBase, type Database, type character, type groupChat } from "../../ts/database";
|
||||||
import { selectedCharID } from "../../ts/stores";
|
import { selectedCharID } from "../../ts/stores";
|
||||||
import { PlusIcon, SmileIcon, TrashIcon, UserIcon, ActivityIcon, BookIcon, LoaderIcon, User, DnaIcon, CurlyBracesIcon } from 'lucide-svelte'
|
import { PlusIcon, SmileIcon, TrashIcon, UserIcon, ActivityIcon, BookIcon, LoaderIcon, User, DnaIcon, CurlyBracesIcon, Volume2Icon } from 'lucide-svelte'
|
||||||
import Check from "../Others/Check.svelte";
|
import Check from "../Others/Check.svelte";
|
||||||
import { addCharEmotion, addingEmotion, getCharImage, rmCharEmotion, selectCharImg, makeGroupImage } from "../../ts/characters";
|
import { addCharEmotion, addingEmotion, getCharImage, rmCharEmotion, selectCharImg, makeGroupImage } from "../../ts/characters";
|
||||||
import LoreBook from "./LoreBookSetting.svelte";
|
import LoreBook from "./LoreBookSetting.svelte";
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
import Help from "../Others/Help.svelte";
|
import Help from "../Others/Help.svelte";
|
||||||
import RegexData from "./RegexData.svelte";
|
import RegexData from "./RegexData.svelte";
|
||||||
import { exportChar } from "src/ts/characterCards";
|
import { exportChar } from "src/ts/characterCards";
|
||||||
|
import { getElevenTTSVoices, getWebSpeechTTSVoices } from "src/ts/process/tts";
|
||||||
|
|
||||||
let subMenu = 0
|
let subMenu = 0
|
||||||
let subberMenu = 0
|
let subberMenu = 0
|
||||||
@@ -157,6 +158,9 @@
|
|||||||
<BookIcon />
|
<BookIcon />
|
||||||
</button>
|
</button>
|
||||||
{#if currentChar.type === 'character'}
|
{#if currentChar.type === 'character'}
|
||||||
|
<button class={subMenu === 5 ? 'text-gray-200' : 'text-gray-500'} on:click={() => {subMenu = 5}}>
|
||||||
|
<Volume2Icon />
|
||||||
|
</button>
|
||||||
<button class={subMenu === 4 ? 'text-gray-200' : 'text-gray-500'} on:click={() => {subMenu = 4}}>
|
<button class={subMenu === 4 ? 'text-gray-200' : 'text-gray-500'} on:click={() => {subMenu = 4}}>
|
||||||
<CurlyBracesIcon />
|
<CurlyBracesIcon />
|
||||||
</button>
|
</button>
|
||||||
@@ -445,6 +449,49 @@
|
|||||||
}
|
}
|
||||||
}}><PlusIcon /></button>
|
}}><PlusIcon /></button>
|
||||||
{/if}
|
{/if}
|
||||||
|
{:else if subMenu === 5}
|
||||||
|
{#if currentChar.type === 'character'}
|
||||||
|
<h2 class="mb-2 text-2xl font-bold mt-2">TTS</h2>
|
||||||
|
<span class="text-neutral-200">{language.provider}</span>
|
||||||
|
<select class="bg-transparent input-text mt-2 mb-4 text-gray-200 appearance-none text-sm" bind:value={currentChar.data.ttsMode} on:change={() => {
|
||||||
|
if(currentChar.type === 'character'){
|
||||||
|
currentChar.data.ttsSpeech = ''
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<option value="" class="bg-darkbg appearance-none">{language.disabled}</option>
|
||||||
|
<option value="elevenlab" class="bg-darkbg appearance-none">ElevenLabs</option>
|
||||||
|
<option value="webspeech" class="bg-darkbg appearance-none">Web Speech</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
{#if currentChar.data.ttsMode === 'webspeech'}
|
||||||
|
{#if !speechSynthesis}
|
||||||
|
<span class="text-neutral-200">Web Speech isn't supported in your browser or OS</span>
|
||||||
|
{:else}
|
||||||
|
<span class="text-neutral-200">{language.Speech}</span>
|
||||||
|
<select class="bg-transparent input-text mt-2 mb-4 text-gray-200 appearance-none text-sm" bind:value={currentChar.data.ttsSpeech}>
|
||||||
|
<option value="" class="bg-darkbg appearance-none">Auto</option>
|
||||||
|
{#each getWebSpeechTTSVoices() as voice}
|
||||||
|
<option value={voice} class="bg-darkbg appearance-none">{voice}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
{#if currentChar.data.ttsSpeech !== ''}
|
||||||
|
<span class="text-red-400 text-sm">If you do not set it to Auto, it may not work properly when importing from another OS or browser.</span>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{:else if currentChar.data.ttsMode === 'elevenlab'}
|
||||||
|
<span class="text-sm mb-2 text-gray-400">Please set the ElevenLabs API key in "global Settings → Bot Settings → Others → ElevenLabs API key"</span>
|
||||||
|
{#await getElevenTTSVoices() then voices}
|
||||||
|
<span class="text-neutral-200">{language.Speech}</span>
|
||||||
|
<select class="bg-transparent input-text mt-2 mb-4 text-gray-200 appearance-none text-sm" bind:value={currentChar.data.ttsSpeech}>
|
||||||
|
<option value="" class="bg-darkbg appearance-none">Unset</option>
|
||||||
|
{#each voices as voice}
|
||||||
|
<option value={voice.voice_id} class="bg-darkbg appearance-none">{voice.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
{:else if subMenu === 2}
|
{:else if subMenu === 2}
|
||||||
<h2 class="mb-2 text-2xl font-bold mt-2">{language.advancedSettings}</h2>
|
<h2 class="mb-2 text-2xl font-bold mt-2">{language.advancedSettings}</h2>
|
||||||
{#if currentChar.type !== 'group'}
|
{#if currentChar.type !== 'group'}
|
||||||
|
|||||||
@@ -277,7 +277,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
<span class="text-neutral-200 mt-4 text-lg font-bold">TTS</span>
|
||||||
|
<span class="text-neutral-200 mt-2">ElevenLabs API key</span>
|
||||||
|
<input class="text-neutral-200 mb-4 p-2 bg-transparent input-text focus:bg-selected text-sm" bind:value={$DataBase.elevenLabKey}>
|
||||||
|
|
||||||
|
|
||||||
{:else if subMenu == 3}
|
{:else if subMenu == 3}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { cloneDeep } from 'lodash';
|
|||||||
|
|
||||||
export const DataBase = writable({} as any as Database)
|
export const DataBase = writable({} as any as Database)
|
||||||
export const loadedStore = writable(false)
|
export const loadedStore = writable(false)
|
||||||
export let appVer = '0.8.2'
|
export let appVer = '0.9.0'
|
||||||
|
|
||||||
|
|
||||||
export function setDatabase(data:Database){
|
export function setDatabase(data:Database){
|
||||||
@@ -181,6 +181,9 @@ export function setDatabase(data:Database){
|
|||||||
if(checkNullish(data.showUnrecommended)){
|
if(checkNullish(data.showUnrecommended)){
|
||||||
data.showUnrecommended = false
|
data.showUnrecommended = false
|
||||||
}
|
}
|
||||||
|
if(checkNullish(data.elevenLabKey)){
|
||||||
|
data.elevenLabKey = ''
|
||||||
|
}
|
||||||
if(checkNullish(data.sdConfig)){
|
if(checkNullish(data.sdConfig)){
|
||||||
data.sdConfig = {
|
data.sdConfig = {
|
||||||
width:512,
|
width:512,
|
||||||
@@ -262,6 +265,8 @@ export interface character{
|
|||||||
creator?:string
|
creator?:string
|
||||||
character_version?:number
|
character_version?:number
|
||||||
}
|
}
|
||||||
|
ttsMode?:string
|
||||||
|
ttsSpeech?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -386,6 +391,7 @@ export interface Database{
|
|||||||
requestmet: string
|
requestmet: string
|
||||||
requestproxy: string
|
requestproxy: string
|
||||||
showUnrecommended:boolean
|
showUnrecommended:boolean
|
||||||
|
elevenLabKey:string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -288,7 +288,10 @@ export async function loadData() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const knownHostes = ["localhost","172.0.0.1"]
|
||||||
|
|
||||||
export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:string]:string}, rawResponse?:boolean, method?:"POST"|"GET"}) {
|
export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:string]:string}, rawResponse?:boolean, method?:"POST"|"GET"}) {
|
||||||
|
try {
|
||||||
const db = get(DataBase)
|
const db = get(DataBase)
|
||||||
const method = arg.method ?? "POST"
|
const method = arg.method ?? "POST"
|
||||||
|
|
||||||
@@ -315,6 +318,46 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const urlHost = (new URL(url)).hostname
|
||||||
|
let forcePlainFetch = knownHostes.includes(urlHost)
|
||||||
|
|
||||||
|
if(db.requestmet === 'plain' || forcePlainFetch){
|
||||||
|
try {
|
||||||
|
let headers = arg.headers ?? {}
|
||||||
|
if(!headers["Content-Type"]){
|
||||||
|
headers["Content-Type"] = `application/json`
|
||||||
|
}
|
||||||
|
const furl = new URL(url)
|
||||||
|
|
||||||
|
const da = await fetch(furl, {
|
||||||
|
body: JSON.stringify(arg.body),
|
||||||
|
headers: arg.headers,
|
||||||
|
method: method
|
||||||
|
})
|
||||||
|
|
||||||
|
if(arg.rawResponse){
|
||||||
|
addFetchLog("Uint8Array Response", da.ok)
|
||||||
|
return {
|
||||||
|
ok: da.ok,
|
||||||
|
data: new Uint8Array(await da.arrayBuffer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
const dat = await da.json()
|
||||||
|
addFetchLog(dat, da.ok)
|
||||||
|
return {
|
||||||
|
ok: da.ok,
|
||||||
|
data: dat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
data: `${error}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if(db.requestmet === 'proxy'){
|
if(db.requestmet === 'proxy'){
|
||||||
try {
|
try {
|
||||||
let headers = arg.headers ?? {}
|
let headers = arg.headers ?? {}
|
||||||
@@ -353,45 +396,6 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(db.requestmet === 'plain'){
|
|
||||||
try {
|
|
||||||
let headers = arg.headers ?? {}
|
|
||||||
if(!headers["Content-Type"]){
|
|
||||||
headers["Content-Type"] = `application/json`
|
|
||||||
}
|
|
||||||
const furl = new URL(url)
|
|
||||||
|
|
||||||
const da = await fetch(furl, {
|
|
||||||
body: JSON.stringify(arg.body),
|
|
||||||
headers: arg.headers,
|
|
||||||
method: method
|
|
||||||
})
|
|
||||||
|
|
||||||
if(arg.rawResponse){
|
|
||||||
addFetchLog("Uint8Array Response", da.ok)
|
|
||||||
return {
|
|
||||||
ok: da.ok,
|
|
||||||
data: new Uint8Array(await da.arrayBuffer())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
const dat = await da.json()
|
|
||||||
addFetchLog(dat, da.ok)
|
|
||||||
return {
|
|
||||||
ok: da.ok,
|
|
||||||
data: dat
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
data: `${error}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if(isTauri){
|
if(isTauri){
|
||||||
if(db.requester === 'new'){
|
if(db.requester === 'new'){
|
||||||
try {
|
try {
|
||||||
@@ -508,6 +512,12 @@ export async function globalFetch(url:string, arg:{body?:any,headers?:{[key:stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
ok:false,
|
||||||
|
data: `${error}`
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const re = /\\/g
|
const re = /\\/g
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { requestChatData } from "./request";
|
|||||||
import { stableDiff } from "./stableDiff";
|
import { stableDiff } from "./stableDiff";
|
||||||
import { processScript, processScriptFull } from "./scripts";
|
import { processScript, processScriptFull } from "./scripts";
|
||||||
import { exampleMessage } from "./exampleMessages";
|
import { exampleMessage } from "./exampleMessages";
|
||||||
|
import { sayTTS } from "./tts";
|
||||||
|
|
||||||
export interface OpenAIChat{
|
export interface OpenAIChat{
|
||||||
role: 'system'|'user'|'assistant'
|
role: 'system'|'user'|'assistant'
|
||||||
@@ -214,9 +215,6 @@ export async function sendChat(chatProcessIndex = -1):Promise<boolean> {
|
|||||||
currentTokens += (await tokenize(systemMsg) + 1)
|
currentTokens += (await tokenize(systemMsg) + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(currentTokens)
|
|
||||||
console.log(maxContextTokens)
|
|
||||||
|
|
||||||
while(currentTokens > maxContextTokens){
|
while(currentTokens > maxContextTokens){
|
||||||
if(chats.length <= 1){
|
if(chats.length <= 1){
|
||||||
alertError(language.errors.toomuchtoken)
|
alertError(language.errors.toomuchtoken)
|
||||||
@@ -228,8 +226,6 @@ export async function sendChat(chatProcessIndex = -1):Promise<boolean> {
|
|||||||
chats.splice(0, 1)
|
chats.splice(0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(currentTokens)
|
|
||||||
|
|
||||||
let bias:{[key:number]:number} = {}
|
let bias:{[key:number]:number} = {}
|
||||||
|
|
||||||
for(let i=0;i<currentChar.bias.length;i++){
|
for(let i=0;i<currentChar.bias.length;i++){
|
||||||
@@ -318,6 +314,7 @@ export async function sendChat(chatProcessIndex = -1):Promise<boolean> {
|
|||||||
data: result,
|
data: result,
|
||||||
saying: currentChar.chaId
|
saying: currentChar.chaId
|
||||||
})
|
})
|
||||||
|
await sayTTS(currentChar, result)
|
||||||
setDatabase(db)
|
setDatabase(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
71
src/ts/process/tts.ts
Normal file
71
src/ts/process/tts.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { get } from "svelte/store";
|
||||||
|
import { alertError } from "../alert";
|
||||||
|
import { DataBase, type character } from "../database";
|
||||||
|
|
||||||
|
export async function sayTTS(character:character,text:string) {
|
||||||
|
|
||||||
|
let db = get(DataBase)
|
||||||
|
|
||||||
|
switch(character.ttsMode){
|
||||||
|
case "webspeech":{
|
||||||
|
if(speechSynthesis && SpeechSynthesisUtterance){
|
||||||
|
const utterThis = new SpeechSynthesisUtterance(text);
|
||||||
|
const voices = speechSynthesis.getVoices();
|
||||||
|
let voiceIndex = 0
|
||||||
|
for(let i=0;i<voices.length;i++){
|
||||||
|
if(voices[i].name === character.ttsSpeech){
|
||||||
|
voiceIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
utterThis.voice = voices[voiceIndex]
|
||||||
|
speechSynthesis.speak(utterThis)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "elevenlab": {
|
||||||
|
const audioContext = new AudioContext();
|
||||||
|
const da = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${character.ttsSpeech}`, {
|
||||||
|
body: JSON.stringify({
|
||||||
|
text: text
|
||||||
|
}),
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
'xi-api-key': db.elevenLabKey || undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if(da.status >= 200 && da.status < 300){
|
||||||
|
const audioBuffer = await audioContext.decodeAudioData(await da.arrayBuffer())
|
||||||
|
const sourceNode = audioContext.createBufferSource();
|
||||||
|
sourceNode.buffer = audioBuffer;
|
||||||
|
sourceNode.connect(audioContext.destination);
|
||||||
|
sourceNode.start();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
alertError(await da.text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function getWebSpeechTTSVoices() {
|
||||||
|
return speechSynthesis.getVoices().map(v => {
|
||||||
|
return v.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getElevenTTSVoices() {
|
||||||
|
let db = get(DataBase)
|
||||||
|
|
||||||
|
const data = await fetch('https://api.elevenlabs.io/v1/voices', {
|
||||||
|
headers: {
|
||||||
|
'xi-api-key': db.elevenLabKey || undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const res = await data.json()
|
||||||
|
|
||||||
|
console.log(res)
|
||||||
|
return res.voices
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
{"version":"0.8.2"}
|
{"version":"0.9.0"}
|
||||||
Reference in New Issue
Block a user