Merge branch 'kwaroran:main' into main
This commit is contained in:
@@ -16,7 +16,7 @@ import {open} from '@tauri-apps/plugin-shell'
|
||||
import { setDatabase, type Database, defaultSdDataFunc, getDatabase, type character } from "./storage/database.svelte";
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { checkRisuUpdate } from "./update";
|
||||
import { MobileGUI, botMakerMode, selectedCharID, loadedStore, DBState } from "./stores.svelte";
|
||||
import { MobileGUI, botMakerMode, selectedCharID, loadedStore, DBState, LoadingStatusState } from "./stores.svelte";
|
||||
import { loadPlugins } from "./plugins/plugins";
|
||||
import { alertConfirm, alertError, alertNormal, alertNormalWait, alertSelect, alertTOS, alertWait } from "./alert";
|
||||
import { checkDriverInit, syncDrive } from "./drive/drive";
|
||||
@@ -42,6 +42,7 @@ import { updateLorebooks } from "./characters";
|
||||
import { initMobileGesture } from "./hotkey";
|
||||
import { fetch as TauriHTTPFetch } from '@tauri-apps/plugin-http';
|
||||
import { moduleUpdate } from "./process/modules";
|
||||
import type { AccountStorage } from "./storage/accountStorage";
|
||||
|
||||
//@ts-ignore
|
||||
export const isTauri = !!window.__TAURI_INTERNALS__
|
||||
@@ -302,6 +303,9 @@ export async function loadAsset(id:string){
|
||||
}
|
||||
|
||||
let lastSave = ''
|
||||
export let saving = $state({
|
||||
state: false
|
||||
})
|
||||
|
||||
/**
|
||||
* Saves the current state of the database.
|
||||
@@ -335,6 +339,7 @@ export async function saveDb(){
|
||||
$effect.root(() => {
|
||||
|
||||
let selIdState = $state(0)
|
||||
let oldSaveHash = ''
|
||||
|
||||
selectedCharID.subscribe((v) => {
|
||||
selIdState = v
|
||||
@@ -347,6 +352,7 @@ export async function saveDb(){
|
||||
$state.snapshot(DBState.db[key])
|
||||
}
|
||||
}
|
||||
|
||||
changed = true
|
||||
})
|
||||
})
|
||||
@@ -356,10 +362,11 @@ export async function saveDb(){
|
||||
await sleep(1000)
|
||||
while(true){
|
||||
if(!changed){
|
||||
await sleep(1000)
|
||||
await sleep(500)
|
||||
continue
|
||||
}
|
||||
|
||||
saving.state = true
|
||||
changed = false
|
||||
|
||||
try {
|
||||
@@ -430,6 +437,8 @@ export async function saveDb(){
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
saving.state = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,6 +499,7 @@ export async function loadData() {
|
||||
if(!loaded){
|
||||
try {
|
||||
if(isTauri){
|
||||
LoadingStatusState.text = "Checking Files..."
|
||||
appWindow.maximize()
|
||||
if(!await exists('', {baseDir: BaseDirectory.AppData})){
|
||||
await mkdir('', {baseDir: BaseDirectory.AppData})
|
||||
@@ -504,13 +514,18 @@ export async function loadData() {
|
||||
await writeFile('database/database.bin', encodeRisuSaveLegacy({}), {baseDir: BaseDirectory.AppData});
|
||||
}
|
||||
try {
|
||||
const decoded = await decodeRisuSave(await readFile('database/database.bin',{baseDir: BaseDirectory.AppData}))
|
||||
LoadingStatusState.text = "Reading Save File..."
|
||||
const readed = await readFile('database/database.bin',{baseDir: BaseDirectory.AppData})
|
||||
LoadingStatusState.text = "Decoding Save File..."
|
||||
const decoded = await decodeRisuSave(readed)
|
||||
setDatabase(decoded)
|
||||
} catch (error) {
|
||||
LoadingStatusState.text = "Reading Backup Files..."
|
||||
const backups = await getDbBackups()
|
||||
let backupLoaded = false
|
||||
for(const backup of backups){
|
||||
try {
|
||||
LoadingStatusState.text = `Reading Backup File ${backup}...`
|
||||
const backupData = await readFile(`database/dbbackup-${backup}.bin`,{baseDir: BaseDirectory.AppData})
|
||||
setDatabase(
|
||||
await decodeRisuSave(backupData)
|
||||
@@ -524,39 +539,19 @@ export async function loadData() {
|
||||
throw "Your save file is corrupted"
|
||||
}
|
||||
}
|
||||
LoadingStatusState.text = "Checking Update..."
|
||||
await checkRisuUpdate()
|
||||
await changeFullscreen()
|
||||
|
||||
}
|
||||
else{
|
||||
let gotStorage:Uint8Array = await forageStorage.getItem('database/database.bin') as unknown as Uint8Array
|
||||
if(checkNullish(gotStorage)){
|
||||
gotStorage = encodeRisuSaveLegacy({})
|
||||
await forageStorage.setItem('database/database.bin', gotStorage)
|
||||
}
|
||||
try {
|
||||
const decoded = await decodeRisuSave(gotStorage)
|
||||
console.log(decoded)
|
||||
setDatabase(decoded)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
const backups = await getDbBackups()
|
||||
let backupLoaded = false
|
||||
for(const backup of backups){
|
||||
try {
|
||||
const backupData:Uint8Array = await forageStorage.getItem(`database/dbbackup-${backup}.bin`) as unknown as Uint8Array
|
||||
setDatabase(
|
||||
await decodeRisuSave(backupData)
|
||||
)
|
||||
backupLoaded = true
|
||||
} catch (error) {}
|
||||
}
|
||||
if(!backupLoaded){
|
||||
throw "Your save file is corrupted"
|
||||
}
|
||||
}
|
||||
await forageStorage.Init()
|
||||
|
||||
if(await forageStorage.checkAccountSync()){
|
||||
let gotStorage:Uint8Array = await forageStorage.getItem('database/database.bin') as unknown as Uint8Array
|
||||
LoadingStatusState.text = "Checking Account Sync..."
|
||||
let gotStorage:Uint8Array = await (forageStorage.realStorage as AccountStorage).getItem('database/database.bin', (v) => {
|
||||
LoadingStatusState.text = `Loading Save File ${(v*100).toFixed(2)}%`
|
||||
})
|
||||
if(checkNullish(gotStorage)){
|
||||
gotStorage = encodeRisuSaveLegacy({})
|
||||
await forageStorage.setItem('database/database.bin', gotStorage)
|
||||
@@ -570,6 +565,7 @@ export async function loadData() {
|
||||
let backupLoaded = false
|
||||
for(const backup of backups){
|
||||
try {
|
||||
LoadingStatusState.text = `Reading Backup File ${backup}...`
|
||||
const backupData:Uint8Array = await forageStorage.getItem(`database/dbbackup-${backup}.bin`) as unknown as Uint8Array
|
||||
setDatabase(
|
||||
await decodeRisuSave(backupData)
|
||||
@@ -582,10 +578,43 @@ export async function loadData() {
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
LoadingStatusState.text = "Loading Save File..."
|
||||
let gotStorage:Uint8Array = await forageStorage.getItem('database/database.bin') as unknown as Uint8Array
|
||||
LoadingStatusState.text = "Decoding Save File..."
|
||||
if(checkNullish(gotStorage)){
|
||||
gotStorage = encodeRisuSaveLegacy({})
|
||||
await forageStorage.setItem('database/database.bin', gotStorage)
|
||||
}
|
||||
try {
|
||||
const decoded = await decodeRisuSave(gotStorage)
|
||||
console.log(decoded)
|
||||
setDatabase(decoded)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
const backups = await getDbBackups()
|
||||
let backupLoaded = false
|
||||
for(const backup of backups){
|
||||
try {
|
||||
LoadingStatusState.text = `Reading Backup File ${backup}...`
|
||||
const backupData:Uint8Array = await forageStorage.getItem(`database/dbbackup-${backup}.bin`) as unknown as Uint8Array
|
||||
setDatabase(
|
||||
await decodeRisuSave(backupData)
|
||||
)
|
||||
backupLoaded = true
|
||||
} catch (error) {}
|
||||
}
|
||||
if(!backupLoaded){
|
||||
throw "Your save file is corrupted"
|
||||
}
|
||||
}
|
||||
}
|
||||
LoadingStatusState.text = "Checking Drive Sync..."
|
||||
const isDriverMode = await checkDriverInit()
|
||||
if(isDriverMode){
|
||||
return
|
||||
}
|
||||
LoadingStatusState.text = "Checking Service Worker..."
|
||||
if(navigator.serviceWorker && (!Capacitor.isNativePlatform())){
|
||||
usingSw = true
|
||||
await registerSw()
|
||||
@@ -597,13 +626,16 @@ export async function loadData() {
|
||||
characterURLImport()
|
||||
}
|
||||
}
|
||||
LoadingStatusState.text = "Checking Unnecessary Files..."
|
||||
try {
|
||||
await pargeChunks()
|
||||
} catch (error) {}
|
||||
LoadingStatusState.text = "Loading Plugins..."
|
||||
try {
|
||||
await loadPlugins()
|
||||
} catch (error) {}
|
||||
if(getDatabase().account){
|
||||
LoadingStatusState.text = "Checking Account Data..."
|
||||
try {
|
||||
await loadRisuAccountData()
|
||||
} catch (error) {}
|
||||
@@ -617,8 +649,11 @@ export async function loadData() {
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
LoadingStatusState.text = "Checking For Format Update..."
|
||||
await checkNewFormat()
|
||||
const db = getDatabase();
|
||||
|
||||
LoadingStatusState.text = "Updating States..."
|
||||
updateColorScheme()
|
||||
updateTextThemeAndCSS()
|
||||
updateAnimationSpeed()
|
||||
@@ -1809,20 +1844,18 @@ const pipeFetchLog = (fetchLogIndex: number, readableStream: ReadableStream<Uint
|
||||
* @throws {Error} - Throws an error if the request is aborted or if there is an error in the response.
|
||||
*/
|
||||
export async function fetchNative(url:string, arg:{
|
||||
body:string|Uint8Array|ArrayBuffer,
|
||||
body?:string|Uint8Array|ArrayBuffer,
|
||||
headers?:{[key:string]:string},
|
||||
method?:"POST"|"GET"|"PUT"|"DELETE",
|
||||
signal?:AbortSignal,
|
||||
useRisuTk?:boolean,
|
||||
chatId?:string
|
||||
}):Promise<{
|
||||
body: ReadableStream<Uint8Array>;
|
||||
headers: Headers;
|
||||
status: number;
|
||||
json: () => Promise<any>;
|
||||
text: () => Promise<string>;
|
||||
arrayBuffer: () => Promise<ArrayBuffer>;
|
||||
}> {
|
||||
}):Promise<Response> {
|
||||
|
||||
console.log(arg.body,'body')
|
||||
if(arg.body === undefined && (arg.method === 'POST' || arg.method === 'PUT') ){
|
||||
throw new Error('Body is required for POST and PUT requests')
|
||||
}
|
||||
|
||||
const jsonizer = (body:ReadableStream<Uint8Array>) => {
|
||||
return async () => {
|
||||
@@ -1864,9 +1897,9 @@ export async function fetchNative(url:string, arg:{
|
||||
let realBody:Uint8Array
|
||||
|
||||
if(arg.method === 'GET' || arg.method === 'DELETE'){
|
||||
realBody = new Uint8Array(0)
|
||||
realBody = undefined
|
||||
}
|
||||
if(typeof arg.body === 'string'){
|
||||
else if(typeof arg.body === 'string'){
|
||||
realBody = new TextEncoder().encode(arg.body)
|
||||
}
|
||||
else if(arg.body instanceof Uint8Array){
|
||||
@@ -1978,18 +2011,15 @@ export async function fetchNative(url:string, arg:{
|
||||
throw new Error(error)
|
||||
}
|
||||
|
||||
return {
|
||||
body: readableStream,
|
||||
return new Response(readableStream, {
|
||||
headers: new Headers(resHeaders),
|
||||
status: status,
|
||||
json: jsonizer(readableStream),
|
||||
text: textizer(readableStream),
|
||||
arrayBuffer: arrayBufferizer(readableStream)
|
||||
}
|
||||
status: status
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
else if(throughProxy){
|
||||
|
||||
const r = await fetch(hubURL + `/proxy2`, {
|
||||
body: realBody,
|
||||
headers: arg.useRisuTk ? {
|
||||
@@ -2006,14 +2036,10 @@ export async function fetchNative(url:string, arg:{
|
||||
signal: arg.signal
|
||||
})
|
||||
|
||||
return {
|
||||
body: pipeFetchLog(fetchLogIndex, r.body),
|
||||
return new Response(r.body, {
|
||||
headers: r.headers,
|
||||
status: r.status,
|
||||
json: jsonizer(r.body),
|
||||
text: textizer(r.body),
|
||||
arrayBuffer: arrayBufferizer(r.body)
|
||||
}
|
||||
status: r.status
|
||||
})
|
||||
}
|
||||
else{
|
||||
return await fetch(url, {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { runTrigger } from "./process/triggers";
|
||||
import { runCharacterJS } from "./plugins/embedscript";
|
||||
import { sleep } from "./util";
|
||||
import { getCurrentCharacter, getCurrentChat, setCurrentChat } from "./storage/database.svelte";
|
||||
|
||||
@@ -36,13 +35,6 @@ function nodeObserve(node:HTMLElement){
|
||||
}
|
||||
|
||||
if(btnEvent){
|
||||
node.addEventListener('click',async ()=>{
|
||||
await runCharacterJS({
|
||||
code: null,
|
||||
mode: 'onButtonClick',
|
||||
data: btnEvent
|
||||
})
|
||||
}, {passive: true})
|
||||
node.setAttribute('risu-observer', 'true');
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2335,9 +2335,17 @@ export function parseChatML(data:string):OpenAIChat[]|null{
|
||||
v = v.substring(0, v.length - ender.length)
|
||||
}
|
||||
|
||||
|
||||
let thoughts:string[] = []
|
||||
v = v.replace(/<Thoughts>(.+)<\/Thoughts>/gms, (match, p1) => {
|
||||
thoughts.push(p1)
|
||||
return ''
|
||||
})
|
||||
|
||||
return {
|
||||
role: role,
|
||||
content: risuChatParser(v)
|
||||
content: risuChatParser(v),
|
||||
thoughts: thoughts
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,324 +0,0 @@
|
||||
import { get } from 'svelte/store'
|
||||
import type { ScriptMode } from '../process/scripts'
|
||||
//@ts-ignore
|
||||
import WorkerUrl from './embedworker?worker&url'
|
||||
import { getDatabase, type Message } from '../storage/database.svelte'
|
||||
import { selectedCharID } from '../stores.svelte'
|
||||
import { setDatabase } from '../storage/database.svelte'
|
||||
|
||||
let worker = new Worker(WorkerUrl, {type: 'module'})
|
||||
|
||||
let additionalCharaJS:string[] = []
|
||||
|
||||
export function addAdditionalCharaJS(code:string, position:'front'|'back' = 'back'){
|
||||
if(position === 'front'){
|
||||
additionalCharaJS.unshift(code)
|
||||
}
|
||||
else{
|
||||
additionalCharaJS.push(code)
|
||||
}
|
||||
}
|
||||
|
||||
let results:{
|
||||
id: string,
|
||||
result: any
|
||||
}[] = []
|
||||
|
||||
let workerFunctions: {
|
||||
[key:string]: (...args:any[])=> Promise<any>
|
||||
} = {}
|
||||
|
||||
worker.onmessage = ({data}) => {
|
||||
if(data.type === 'api'){
|
||||
workerFunctions[data.name](...data.args).then((result)=>{
|
||||
worker.postMessage({
|
||||
type: 'result',
|
||||
id: data.id,
|
||||
result
|
||||
})
|
||||
})
|
||||
}
|
||||
else{
|
||||
results.push(data)
|
||||
}
|
||||
}
|
||||
|
||||
function addWorkerFunction(name:string, func: (...args:any[])=> Promise<any>){
|
||||
workerFunctions[name] = func
|
||||
worker.postMessage({
|
||||
type: 'api',
|
||||
name
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function runVirtualJS(code:string){
|
||||
const id = `id${Math.random()}`.replace('.','')
|
||||
worker.postMessage({
|
||||
id,code
|
||||
})
|
||||
let startTime = performance.now()
|
||||
return new Promise((resolve,reject)=>{
|
||||
const interval = setInterval(()=>{
|
||||
const result = results.find(r=>r.id === id)
|
||||
if(result){
|
||||
clearInterval(interval)
|
||||
resolve(result.result)
|
||||
}
|
||||
else if(performance.now() - startTime > 800){
|
||||
clearInterval(interval)
|
||||
//restart worker
|
||||
worker.terminate()
|
||||
worker = new Worker(WorkerUrl, {type: 'module'})
|
||||
reject('timeout')
|
||||
}
|
||||
},10)
|
||||
})
|
||||
}
|
||||
|
||||
addWorkerFunction('getChat', async () => {
|
||||
const db = getDatabase({
|
||||
snapshot: true
|
||||
})
|
||||
const selectedChar = get(selectedCharID)
|
||||
const char = db.characters[selectedChar]
|
||||
return safeStructuredClone(char.chats[char.chatPage].message)
|
||||
})
|
||||
|
||||
addWorkerFunction('setChat', async (data:Message[]) => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
let newChat:Message[] = []
|
||||
for(const dat of data){
|
||||
if(dat.role !== 'char' && dat.role !== 'user'){
|
||||
return false
|
||||
}
|
||||
if(typeof dat.data !== 'string'){
|
||||
return false
|
||||
}
|
||||
if(typeof dat.saying !== 'string' && dat.saying !== null && dat.saying !== undefined){
|
||||
return false
|
||||
}
|
||||
if(typeof dat.time !== 'number' && dat.time !== null && dat.time !== undefined){
|
||||
return false
|
||||
}
|
||||
if(typeof dat.chatId !== 'string' && dat.chatId !== null && dat.chatId !== undefined){
|
||||
return false
|
||||
}
|
||||
newChat.push({
|
||||
role: dat.role,
|
||||
data: dat.data,
|
||||
saying: dat.saying,
|
||||
time: dat.time,
|
||||
chatId: dat.chatId
|
||||
})
|
||||
}
|
||||
db.characters[selectedChar].chats[db.characters[selectedChar].chatPage].message = newChat
|
||||
setDatabase(db)
|
||||
return true
|
||||
})
|
||||
|
||||
addWorkerFunction('getName', async () => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
const char = db.characters[selectedChar]
|
||||
return char.name
|
||||
})
|
||||
|
||||
addWorkerFunction('setName', async (data:string) => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
if(typeof data !== 'string'){
|
||||
return false
|
||||
}
|
||||
db.characters[selectedChar].name = data
|
||||
setDatabase(db)
|
||||
return true
|
||||
})
|
||||
|
||||
addWorkerFunction('getDescription', async () => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
const char = db.characters[selectedChar]
|
||||
if(char.type === 'group'){
|
||||
return ''
|
||||
}
|
||||
return char.desc
|
||||
})
|
||||
|
||||
addWorkerFunction('setDescription', async (data:string) => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
const char =db.characters[selectedChar]
|
||||
if(typeof data !== 'string'){
|
||||
return false
|
||||
}
|
||||
if(char.type === 'group'){
|
||||
return false
|
||||
}
|
||||
char.desc = data
|
||||
db.characters[selectedChar] = char
|
||||
setDatabase(db)
|
||||
return true
|
||||
})
|
||||
|
||||
addWorkerFunction('getCharacterFirstMessage', async () => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
const char = db.characters[selectedChar]
|
||||
return char.firstMessage
|
||||
})
|
||||
|
||||
addWorkerFunction('setCharacterFirstMessage', async (data:string) => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
const char = db.characters[selectedChar]
|
||||
if(typeof data !== 'string'){
|
||||
return false
|
||||
}
|
||||
char.firstMessage = data
|
||||
db.characters[selectedChar] = char
|
||||
setDatabase(db)
|
||||
return true
|
||||
})
|
||||
|
||||
addWorkerFunction('getBackgroundEmbedding', async () => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
const char = db.characters[selectedChar]
|
||||
return char.backgroundHTML
|
||||
})
|
||||
|
||||
addWorkerFunction('setBackgroundEmbedding', async (data:string) => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
if(typeof data !== 'string'){
|
||||
return false
|
||||
}
|
||||
db.characters[selectedChar].backgroundHTML = data
|
||||
setDatabase(db)
|
||||
return true
|
||||
})
|
||||
|
||||
|
||||
addWorkerFunction('getState', async (statename) => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
const char = db.characters[selectedChar]
|
||||
const chat = char.chats[char.chatPage]
|
||||
return (chat.scriptstate ?? {})[statename]
|
||||
})
|
||||
|
||||
addWorkerFunction('setState', async (statename, data) => {
|
||||
const db = getDatabase()
|
||||
const selectedChar = get(selectedCharID)
|
||||
const char = db.characters[selectedChar]
|
||||
const chat = char.chats[char.chatPage]
|
||||
if(typeof statename !== 'string'){
|
||||
return false
|
||||
}
|
||||
if(typeof data !== 'string' && typeof data !== 'number' && typeof data !== 'boolean'){
|
||||
return false
|
||||
}
|
||||
if(typeof data === 'string' && data.length > 100000){
|
||||
return false
|
||||
}
|
||||
if(!chat.scriptstate){
|
||||
chat.scriptstate = {}
|
||||
}
|
||||
if(Object.keys(chat.scriptstate).length > 50){
|
||||
return false
|
||||
}
|
||||
chat.scriptstate[statename] = data
|
||||
char.chats[char.chatPage] = chat
|
||||
db.characters[selectedChar] = char
|
||||
setDatabase(db)
|
||||
return true
|
||||
})
|
||||
|
||||
|
||||
|
||||
let compCode:{[key:string]:string[]} = {}
|
||||
|
||||
export async function runCharacterJS(arg:{
|
||||
code: string|null,
|
||||
mode: ScriptMode|'onButtonClick'|'modifyRequestChat'
|
||||
data: any
|
||||
}):Promise<any>{
|
||||
const perf = performance.now()
|
||||
try {
|
||||
arg.code = arg.code ?? ''
|
||||
const codes = {
|
||||
"editinput": 'editInput',
|
||||
"editoutput": 'editOutput',
|
||||
"editprocess": 'editProcess',
|
||||
"editdisplay": 'editDisplay',
|
||||
'onButtonClick': "onButtonClick",
|
||||
'modifyRequestChat': 'modifyRequestChat'
|
||||
} as const
|
||||
|
||||
let runCodes = [...additionalCharaJS, arg.code]
|
||||
|
||||
let r = arg.data
|
||||
|
||||
for(const code of runCodes){
|
||||
if(!code){
|
||||
continue
|
||||
}
|
||||
if(!compCode[code]){
|
||||
let modeList:string[] = []
|
||||
const codesplit = code.split('\n')
|
||||
for(let i = 0; i < codesplit.length; i++){
|
||||
const line = codesplit[i].trim()
|
||||
if(line.startsWith('//@use')){
|
||||
modeList.push(line.replace('//@use','').trim())
|
||||
}
|
||||
}
|
||||
compCode[code] = modeList
|
||||
// compcode length max 100
|
||||
if(Object.keys(compCode).length > 100){
|
||||
delete compCode[Object.keys(compCode)[50]]
|
||||
}
|
||||
}
|
||||
|
||||
const runCode = codes[arg.mode]
|
||||
|
||||
if(!compCode[code].includes(runCode)){
|
||||
continue
|
||||
}
|
||||
const result = await runVirtualJS(`${code}\n${runCode}(${JSON.stringify(r)})`)
|
||||
|
||||
if(!result){
|
||||
continue
|
||||
}
|
||||
|
||||
if(arg.mode !== 'modifyRequestChat'){
|
||||
if(typeof result !== 'string'){
|
||||
continue
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(!Array.isArray(result)){
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
r = result
|
||||
|
||||
if(runCode === 'onButtonClick'){
|
||||
return r
|
||||
}
|
||||
}
|
||||
return r
|
||||
|
||||
} catch (error) {
|
||||
if(arg.mode !== 'editprocess'){
|
||||
return `Error: ${error}`
|
||||
}
|
||||
return arg.data
|
||||
}
|
||||
finally{
|
||||
console.log('runCharacterJS',performance.now() - perf)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
let globaly = globalThis
|
||||
|
||||
const whitelist = [
|
||||
"Array",
|
||||
"ArrayBuffer",
|
||||
"BigInt",
|
||||
"BigInt64Array",
|
||||
"BigUint64Array",
|
||||
"Boolean",
|
||||
"DataView",
|
||||
"Date",
|
||||
"Error",
|
||||
"EvalError",
|
||||
"Float32Array",
|
||||
"Float64Array",
|
||||
"Function",
|
||||
"Infinity",
|
||||
"Int16Array",
|
||||
"Int32Array",
|
||||
"Int8Array",
|
||||
"JSON",
|
||||
"Map",
|
||||
"Math",
|
||||
"NaN",
|
||||
"Number",
|
||||
"Object",
|
||||
"Promise",
|
||||
"Proxy",
|
||||
"RangeError",
|
||||
"ReferenceError",
|
||||
"Reflect",
|
||||
"RegExp",
|
||||
"Set",
|
||||
"SharedArrayBuffer",
|
||||
"String",
|
||||
"Symbol",
|
||||
"SyntaxError",
|
||||
"TypeError",
|
||||
"URIError",
|
||||
"Uint16Array",
|
||||
"Uint32Array",
|
||||
"Uint8Array",
|
||||
"Uint8ClampedArray",
|
||||
"WeakMap",
|
||||
"WeakSet",
|
||||
"console",
|
||||
"decodeURI",
|
||||
"decodeURIComponent",
|
||||
"encodeURI",
|
||||
"encodeURIComponent",
|
||||
"escape",
|
||||
"globalThis",
|
||||
"isFinite",
|
||||
"isNaN",
|
||||
"null",
|
||||
"parseFloat",
|
||||
"parseInt",
|
||||
"undefined",
|
||||
"unescape",
|
||||
"queueMicrotask",
|
||||
"setTimeout",
|
||||
"clearTimeout",
|
||||
"setInterval",
|
||||
"clearInterval",
|
||||
"setImmediate",
|
||||
"clearImmediate",
|
||||
"atob",
|
||||
"btoa",
|
||||
"Headers",
|
||||
"Request",
|
||||
"Response",
|
||||
"Blob",
|
||||
"postMessage",
|
||||
"Node",
|
||||
"Element",
|
||||
"Text",
|
||||
"Comment",
|
||||
'onmessage',
|
||||
"DOMParser",
|
||||
"XMLSerializer",
|
||||
"TextEncoder",
|
||||
"TextDecoder",
|
||||
"AbortController",
|
||||
"AbortSignal",
|
||||
"Event",
|
||||
"CustomEvent",
|
||||
"EventTarget",
|
||||
"OffscreenCanvas",
|
||||
"ImageBitmap",
|
||||
"ImageBitmapRenderingContext",
|
||||
"createImageBitmap",
|
||||
"OffScreenCanvasRenderingContext2D",
|
||||
"WebGL2RenderingContext",
|
||||
"WebGLRenderingContext",
|
||||
]
|
||||
|
||||
const evaluation = globaly.eval
|
||||
|
||||
const propa = Object.getOwnPropertyNames( this ?? {} )
|
||||
const prop = Object.getOwnPropertyNames( globaly ).concat( propa)
|
||||
prop.push(
|
||||
//unsafe apis
|
||||
'open',
|
||||
'close',
|
||||
'alert',
|
||||
'confirm',
|
||||
'prompt',
|
||||
'print',
|
||||
'fetch',
|
||||
'navigator',
|
||||
'Worker',
|
||||
'WebSocket',
|
||||
'XMLHttpRequest',
|
||||
'localStorage',
|
||||
'sessionStorage',
|
||||
'importScripts',
|
||||
'indexedDB',
|
||||
'crypto',
|
||||
'WebAssembly',
|
||||
'WebSqlDatabase',
|
||||
)
|
||||
|
||||
prop.forEach( function( prop ) {
|
||||
if( (!whitelist.includes(prop)) && (!prop.startsWith('HTML')) ) {
|
||||
try {
|
||||
Object.defineProperty( globaly, prop, {
|
||||
get : function() {
|
||||
throw "Security Exception: cannot access "+prop;
|
||||
return 1;
|
||||
},
|
||||
configurable : false
|
||||
});
|
||||
} catch (error) {
|
||||
}
|
||||
try {
|
||||
Object.defineProperty( this, prop, {
|
||||
get : function() {
|
||||
throw "Security Exception: cannot access "+prop;
|
||||
return 1;
|
||||
},
|
||||
configurable : false
|
||||
});
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
else{
|
||||
}
|
||||
});
|
||||
|
||||
let workerResults:{
|
||||
id: string,
|
||||
result: any
|
||||
}[] = []
|
||||
|
||||
|
||||
self.onmessage = async (event) => {
|
||||
const da = event.data
|
||||
if(da.type === 'result'){
|
||||
workerResults.push(da)
|
||||
return
|
||||
}
|
||||
if(da.type === 'api'){
|
||||
//add api
|
||||
Object.defineProperty( globaly, da.name, {
|
||||
get : function() {
|
||||
return function (...args:any[]) {
|
||||
return new Promise((resolve)=>{
|
||||
const functionCallID = `id${Math.random()}`.replace('.','')
|
||||
self.postMessage({
|
||||
type: 'api',
|
||||
name: da.name,
|
||||
id: functionCallID,
|
||||
args
|
||||
})
|
||||
const interval = setInterval(()=>{
|
||||
const result = workerResults.find(r=>r.id === functionCallID)
|
||||
if(result){
|
||||
clearInterval(interval)
|
||||
resolve(result.result)
|
||||
}
|
||||
},10)
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
return
|
||||
}
|
||||
try{
|
||||
const d = await evaluation(da.code)
|
||||
self.postMessage({
|
||||
id: da.id,
|
||||
result: d
|
||||
})
|
||||
}
|
||||
catch(e){
|
||||
console.error(e)
|
||||
self.postMessage({
|
||||
id: da.id,
|
||||
result: e
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
import { get, writable } from "svelte/store";
|
||||
import { language } from "../../lang";
|
||||
import { alertError } from "../alert";
|
||||
import { alertError, alertMd } from "../alert";
|
||||
import { getCurrentCharacter, getDatabase, setDatabaseLite } from "../storage/database.svelte";
|
||||
import { checkNullish, selectSingleFile, sleep } from "../util";
|
||||
import type { OpenAIChat } from "../process/index.svelte";
|
||||
import { fetchNative, globalFetch } from "../globalApi.svelte";
|
||||
import { selectedCharID } from "../stores.svelte";
|
||||
import { addAdditionalCharaJS } from "./embedscript";
|
||||
import type { ScriptMode } from "../process/scripts";
|
||||
|
||||
export const customProviderStore = writable([] as string[])
|
||||
@@ -33,40 +32,30 @@ export async function importPlugin(){
|
||||
const jsFile = Buffer.from(f.data).toString('utf-8').replace(/^\uFEFF/gm, "");
|
||||
const splitedJs = jsFile.split('\n')
|
||||
let name = ''
|
||||
let version:1|2 = 1
|
||||
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()
|
||||
alertMd('V1 plugin is not supported anymore, please use V2 plugin instead. for more information, please check the documentation. `https://github.com/kwaroran/RisuAI/blob/main/plugins.md`')
|
||||
return
|
||||
}
|
||||
if(line.startsWith('//@risu-display-name')){
|
||||
alertMd('V1 plugin is not supported anymore, please use V2 plugin instead. for more information, please check the documentation. `https://github.com/kwaroran/RisuAI/blob/main/plugins.md`')
|
||||
return
|
||||
}
|
||||
if(line.startsWith('//@name')){
|
||||
const provied = line.slice(7)
|
||||
if(provied === ''){
|
||||
alertError('plugin name must be longer than "", did you put it correctly?')
|
||||
alertError('plugin name must be longer than 0, did you put it correctly?')
|
||||
return
|
||||
}
|
||||
version = 2
|
||||
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
|
||||
}
|
||||
displayName = provied.trim()
|
||||
}
|
||||
if(line.startsWith('//@display-name')){
|
||||
const provied = line.slice('//@display-name'.length + 1)
|
||||
if(provied === ''){
|
||||
alertError('plugin display name must be longer than "", did you put it correctly?')
|
||||
alertError('plugin display name must be longer than 0, did you put it correctly?')
|
||||
return
|
||||
}
|
||||
displayName = provied.trim()
|
||||
@@ -105,7 +94,7 @@ export async function importPlugin(){
|
||||
realArg: realArg,
|
||||
arguments: arg,
|
||||
displayName: displayName,
|
||||
version: version
|
||||
version: 2
|
||||
}
|
||||
|
||||
db.plugins ??= []
|
||||
@@ -119,174 +108,15 @@ export async function importPlugin(){
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentPluginMax(prov:string){
|
||||
return 12000
|
||||
}
|
||||
|
||||
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 = {
|
||||
type: type,
|
||||
body: body
|
||||
}
|
||||
pluginWorker.postMessage(bod)
|
||||
}
|
||||
|
||||
let pluginTranslator = false
|
||||
|
||||
export async function loadPlugins() {
|
||||
let db = getDatabase()
|
||||
|
||||
if(pluginWorker){
|
||||
pluginWorker.terminate()
|
||||
pluginWorker = null
|
||||
}
|
||||
|
||||
const plugins = safeStructuredClone(db.plugins).filter((a:RisuPlugin) => a.version === 1)
|
||||
const pluginV2 = safeStructuredClone(db.plugins).filter((a:RisuPlugin) => a.version === 2)
|
||||
|
||||
await loadV2Plugin(pluginV2)
|
||||
|
||||
if(plugins.length > 0){
|
||||
|
||||
const da = await fetch("/pluginApi.js")
|
||||
const pluginApiString = await da.text()
|
||||
let pluginjs = `${pluginApiString}\n`
|
||||
let pluginLoadedJs = ''
|
||||
|
||||
for(const plug of db.plugins){
|
||||
pluginLoadedJs += `\n(() => {${plug.script}})();\n`
|
||||
}
|
||||
pluginjs = pluginjs.replace('//{{placeholder}}',pluginLoadedJs)
|
||||
|
||||
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 "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
|
||||
break
|
||||
}
|
||||
case "fetch": {
|
||||
postMsgPluginWorker('fetchData',{
|
||||
id: data.body.id,
|
||||
data: await globalFetch(data.body.url, data.body.arg)
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'addCharaJs': {
|
||||
let c:string = data.body.code
|
||||
c.trim()
|
||||
if(c.startsWith('{') && c.endsWith('}')){
|
||||
c = c.slice(1, -1)
|
||||
}
|
||||
addAdditionalCharaJS(c, data.body.position)
|
||||
break
|
||||
}
|
||||
case "getArg":{
|
||||
try {
|
||||
const db = getDatabase()
|
||||
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 "getChar":{
|
||||
const db = getDatabase()
|
||||
const charid = get(selectedCharID)
|
||||
const char = db.characters[charid]
|
||||
postMsgPluginWorker('fetchData',{
|
||||
id: data.body.id,
|
||||
data: char
|
||||
})
|
||||
break
|
||||
}
|
||||
case "setChar":{
|
||||
const db = getDatabase()
|
||||
const charid = get(selectedCharID)
|
||||
db.characters[charid] = data.body
|
||||
break
|
||||
}
|
||||
case "log":{
|
||||
console.log(data.body)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type PluginV2ProviderArgument = {
|
||||
@@ -301,11 +131,17 @@ type PluginV2ProviderArgument = {
|
||||
mode: string
|
||||
}
|
||||
|
||||
type PluginV2ProviderOptions = {
|
||||
tokenizer?: string
|
||||
tokenizerFunc?: (content:string) => number[]|Promise<number[]>
|
||||
}
|
||||
|
||||
type EditFunction = (content:string) => string|null|undefined|Promise<string|null|undefined>
|
||||
type ReplacerFunction = (content:OpenAIChat[], type:string) => OpenAIChat[]|Promise<OpenAIChat[]>
|
||||
|
||||
export const pluginV2 = {
|
||||
providers: new Map<string, (arg:PluginV2ProviderArgument) => Promise<{success:boolean,content:string}> >(),
|
||||
providers: new Map<string, (arg:PluginV2ProviderArgument) => Promise<{success:boolean,content:string|ReadableStream<string>}> >(),
|
||||
providerOptions: new Map<string, PluginV2ProviderOptions>(),
|
||||
editdisplay: new Set<EditFunction>(),
|
||||
editoutput: new Set<EditFunction>(),
|
||||
editprocess: new Set<EditFunction>(),
|
||||
@@ -336,8 +172,9 @@ export async function loadV2Plugin(plugins:RisuPlugin[]){
|
||||
risuFetch: globalFetch,
|
||||
nativeFetch: fetchNative,
|
||||
getArg: (arg:string) => {
|
||||
const db = getDatabase()
|
||||
const [name, realArg] = arg.split('::')
|
||||
for(const plug of plugins){
|
||||
for(const plug of db.plugins){
|
||||
if(plug.name === name){
|
||||
return plug.realArg[realArg]
|
||||
}
|
||||
@@ -352,10 +189,11 @@ export async function loadV2Plugin(plugins:RisuPlugin[]){
|
||||
db.characters[charid] = char
|
||||
setDatabaseLite(db)
|
||||
},
|
||||
addProvider: (name:string, func:(arg:PluginV2ProviderArgument) => Promise<{success:boolean,content:string}>) => {
|
||||
addProvider: (name:string, func:(arg:PluginV2ProviderArgument) => Promise<{success:boolean,content:string}>, options?:PluginV2ProviderOptions) => {
|
||||
let provs = get(customProviderStore)
|
||||
provs.push(name)
|
||||
pluginV2.providers.set(name, func)
|
||||
pluginV2.providerOptions.set(name, options ?? {})
|
||||
customProviderStore.set(provs)
|
||||
},
|
||||
addRisuScriptHandler: (name:ScriptMode, func:EditFunction) => {
|
||||
@@ -407,6 +245,8 @@ export async function loadV2Plugin(plugins:RisuPlugin[]){
|
||||
const setChar = globalThis.__pluginApis__.setChar
|
||||
const addProvider = globalThis.__pluginApis__.addProvider
|
||||
const addRisuEventHandler = globalThis.__pluginApis__.addRisuEventHandler
|
||||
const addRisuReplacer = globalThis.__pluginApis__.addRisuReplacer
|
||||
const removeRisuReplacer = globalThis.__pluginApis__.removeRisuReplacer
|
||||
const onUnload = globalThis.__pluginApis__.onUnload
|
||||
|
||||
${data}
|
||||
@@ -424,30 +264,7 @@ export async function loadV2Plugin(plugins:RisuPlugin[]){
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export async function pluginProcess(arg:{
|
||||
@@ -458,35 +275,10 @@ export async function pluginProcess(arg:{
|
||||
frequency_penalty: number
|
||||
bias: {[key:string]:string}
|
||||
}|{}){
|
||||
try {
|
||||
let db = getDatabase()
|
||||
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"
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
content: "Plugin V1 is not supported anymore, please use V2 plugin instead."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import { getInlayAsset, supportsInlayImage } from "./files/inlays";
|
||||
import { getGenerationModelString } from "./models/modelString";
|
||||
import { connectionOpen, peerRevertChat, peerSafeCheck, peerSync } from "../sync/multiuser";
|
||||
import { runInlayScreen } from "./inlayScreen";
|
||||
import { runCharacterJS } from "../plugins/embedscript";
|
||||
import { addRerolls } from "./prereroll";
|
||||
import { runImageEmbedding } from "./transformers";
|
||||
import { hanuraiMemory } from "./memory/hanuraiMemory";
|
||||
@@ -30,7 +29,6 @@ import { hypaMemoryV2 } from "./memory/hypav2";
|
||||
import { runLuaEditTrigger } from "./lua";
|
||||
import { parseChatML } from "../parser.svelte";
|
||||
import { getModelInfo, LLMFlags } from "../model/modellist";
|
||||
import { pluginV2 } from "../plugins/plugins";
|
||||
|
||||
export interface OpenAIChat{
|
||||
role: 'system'|'user'|'assistant'|'function'
|
||||
@@ -755,7 +753,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{
|
||||
}
|
||||
}
|
||||
let thoughts:string[] = []
|
||||
formatedChat = formatedChat.replace(/<Thoughts>(.+?)<\/Thoughts>/gm, (match, p1) => {
|
||||
formatedChat = formatedChat.replace(/<Thoughts>(.+)<\/Thoughts>/gms, (match, p1) => {
|
||||
thoughts.push(p1)
|
||||
return ''
|
||||
})
|
||||
@@ -1116,12 +1114,6 @@ export async function sendChat(chatProcessIndex = -1,arg:{
|
||||
})
|
||||
}
|
||||
|
||||
formated = await runCharacterJS({
|
||||
code: null,
|
||||
mode: 'modifyRequestChat',
|
||||
data: formated
|
||||
})
|
||||
|
||||
formated = await runLuaEditTrigger(currentChar, 'editRequest', formated)
|
||||
|
||||
//token rechecking
|
||||
|
||||
@@ -279,10 +279,6 @@ export function getModules(){
|
||||
if (currentChat){
|
||||
ids = ids.concat(currentChat.modules ?? [])
|
||||
}
|
||||
if(db.moduleIntergration){
|
||||
const intList = db.moduleIntergration.split(',').map((s) => s.trim())
|
||||
ids = ids.concat(intList)
|
||||
}
|
||||
const idsJoined = ids.join('-')
|
||||
if(lastModules === idsJoined){
|
||||
return lastModuleData
|
||||
|
||||
@@ -259,6 +259,7 @@ export interface OpenAIChatExtra {
|
||||
removable?:boolean
|
||||
attr?:string[]
|
||||
multimodals?:MultiModal[]
|
||||
thoughts?:string[]
|
||||
}
|
||||
|
||||
function reformater(formated:OpenAIChat[],modelInfo:LLMModel){
|
||||
@@ -451,6 +452,7 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
|
||||
delete formatedChat[i].removable
|
||||
delete formatedChat[i].attr
|
||||
delete formatedChat[i].multimodals
|
||||
delete formatedChat[i].thoughts
|
||||
}
|
||||
if(aiModel === 'reverse_proxy' && db.reverseProxyOobaMode && formatedChat[i].role === 'system'){
|
||||
const cont = formatedChat[i].content
|
||||
@@ -1390,43 +1392,69 @@ async function requestOoba(arg:RequestDataArgumentExtended):Promise<requestDataR
|
||||
}
|
||||
|
||||
async function requestPlugin(arg:RequestDataArgumentExtended):Promise<requestDataResponse> {
|
||||
const formated = arg.formated
|
||||
const db = getDatabase()
|
||||
const maxTokens = arg.maxTokens
|
||||
const bias = arg.biasString
|
||||
const v2Function = pluginV2.providers.get(db.currentPluginProvider)
|
||||
|
||||
const d = v2Function ? (await v2Function(applyParameters({
|
||||
prompt_chat: formated,
|
||||
mode: arg.mode,
|
||||
bias: []
|
||||
}, [
|
||||
'frequency_penalty','min_p','presence_penalty','repetition_penalty','top_k','top_p','temperature'
|
||||
], {}, arg.mode) as any)) : 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){
|
||||
try {
|
||||
const formated = arg.formated
|
||||
const maxTokens = arg.maxTokens
|
||||
const bias = arg.biasString
|
||||
const v2Function = pluginV2.providers.get(db.currentPluginProvider)
|
||||
|
||||
const d = v2Function ? (await v2Function(applyParameters({
|
||||
prompt_chat: formated,
|
||||
mode: arg.mode,
|
||||
bias: [],
|
||||
max_tokens: maxTokens,
|
||||
}, [
|
||||
'frequency_penalty','min_p','presence_penalty','repetition_penalty','top_k','top_p','temperature'
|
||||
], {}, arg.mode) as any)) : 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 instanceof ReadableStream ? await (new Response(d.content)).text() : d.content
|
||||
}
|
||||
}
|
||||
else if(d.content instanceof ReadableStream){
|
||||
|
||||
let fullText = ''
|
||||
const piper = new TransformStream<string, StreamResponseChunk>( {
|
||||
transform(chunk, control) {
|
||||
fullText += chunk
|
||||
control.enqueue({
|
||||
"0": fullText
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
type: 'streaming',
|
||||
result: d.content.pipeThrough(piper)
|
||||
}
|
||||
}
|
||||
else{
|
||||
return {
|
||||
type: 'success',
|
||||
result: d.content
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return {
|
||||
type: 'fail',
|
||||
result: (language.errors.unknownModel)
|
||||
}
|
||||
}
|
||||
else if(!d.success){
|
||||
return {
|
||||
type: 'fail',
|
||||
result: d.content
|
||||
}
|
||||
}
|
||||
else{
|
||||
return {
|
||||
type: 'success',
|
||||
result: d.content
|
||||
result: `Plugin Error from ${db.currentPluginProvider}: ` + JSON.stringify(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { alertError, alertNormal } from "../alert";
|
||||
import { language } from "src/lang";
|
||||
import { selectSingleFile } from "../util";
|
||||
import { assetRegex, type CbsConditions, risuChatParser as risuChatParserOrg, type simpleCharacterArgument } from "../parser.svelte";
|
||||
import { runCharacterJS } from "../plugins/embedscript";
|
||||
import { getModuleAssets, getModuleRegexScripts } from "./modules";
|
||||
import { HypaProcesser } from "./memory/hypamemory";
|
||||
import { runLuaEditTrigger } from "./lua";
|
||||
@@ -98,17 +97,12 @@ export function resetScriptCache(){
|
||||
export async function processScriptFull(char:character|groupChat|simpleCharacterArgument, data:string, mode:ScriptMode, chatID = -1, cbsConditions:CbsConditions = {}){
|
||||
let db = getDatabase()
|
||||
const originalData = data
|
||||
const cached = getScriptCache((db.globalscript ?? []).concat(char.customscript), originalData, mode)
|
||||
const cached = getScriptCache((db.presetRegex ?? []).concat(char.customscript), originalData, mode)
|
||||
if(cached){
|
||||
return {data: cached, emoChanged: false}
|
||||
}
|
||||
let emoChanged = false
|
||||
const scripts = (db.globalscript ?? []).concat(char.customscript).concat(getModuleRegexScripts())
|
||||
data = await runCharacterJS({
|
||||
code: char.virtualscript ?? null,
|
||||
mode,
|
||||
data,
|
||||
})
|
||||
const scripts = (db.presetRegex ?? []).concat(char.customscript).concat(getModuleRegexScripts())
|
||||
data = await runLuaEditTrigger(char, mode, data)
|
||||
if(pluginV2[mode].size > 0){
|
||||
for(const plugin of pluginV2[mode]){
|
||||
|
||||
@@ -2,7 +2,7 @@ import { get } from "svelte/store"
|
||||
import { getDatabase, type character } from "../storage/database.svelte"
|
||||
import { requestChatData } from "./request"
|
||||
import { alertError } from "../alert"
|
||||
import { globalFetch, readImage } from "../globalApi.svelte"
|
||||
import { fetchNative, globalFetch, readImage } from "../globalApi.svelte"
|
||||
import { CharEmotion } from "../stores.svelte"
|
||||
import type { OpenAIChat } from "./index.svelte"
|
||||
import { processZip } from "./processzip"
|
||||
@@ -415,12 +415,14 @@ export async function generateAIImage(genPrompt:string, currentChar:character, n
|
||||
|
||||
|
||||
}
|
||||
if(db.sdProvider === 'comfy'){
|
||||
|
||||
if(db.sdProvider === 'comfy' || db.sdProvider === 'comfyui'){
|
||||
const legacy = db.sdProvider === 'comfy' // Legacy Comfy mode
|
||||
const {workflow, posNodeID, posInputName, negNodeID, negInputName} = db.comfyConfig
|
||||
const baseUrl = new URL(db.comfyUiUrl)
|
||||
|
||||
const createUrl = (pathname: string, params: Record<string, string> = {}) => {
|
||||
const url = new URL(pathname, baseUrl)
|
||||
const url = db.comfyUiUrl.endsWith('/api') ? new URL(`${db.comfyUiUrl}${pathname}`) : new URL(pathname, baseUrl)
|
||||
url.search = new URLSearchParams(params).toString()
|
||||
return url.toString()
|
||||
}
|
||||
@@ -437,8 +439,31 @@ export async function generateAIImage(genPrompt:string, currentChar:character, n
|
||||
|
||||
try {
|
||||
const prompt = JSON.parse(workflow)
|
||||
prompt[posNodeID].inputs[posInputName] = genPrompt
|
||||
prompt[negNodeID].inputs[negInputName] = neg
|
||||
if(legacy){
|
||||
prompt[posNodeID].inputs[posInputName] = genPrompt
|
||||
prompt[negNodeID].inputs[negInputName] = neg
|
||||
}
|
||||
else{
|
||||
//search all nodes for the prompt and negative prompt
|
||||
const keys = Object.keys(prompt)
|
||||
for(let i = 0; i < keys.length; i++){
|
||||
const node = prompt[keys[i]]
|
||||
const inputKeys = Object.keys(node.inputs)
|
||||
for(let j = 0; j < inputKeys.length; j++){
|
||||
let input = node.inputs[inputKeys[j]]
|
||||
if(typeof input === 'string'){
|
||||
input = input.replaceAll('{{risu_prompt}}', genPrompt)
|
||||
input = input.replaceAll('{{risu_neg}}', neg)
|
||||
}
|
||||
|
||||
if(inputKeys[j] === 'seed' && typeof input === 'number'){
|
||||
input = Math.floor(Math.random() * 1000000000)
|
||||
}
|
||||
|
||||
node.inputs[inputKeys[j]] = input
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { prompt_id: id } = await fetchWrapper(createUrl('/prompt'), {
|
||||
method: 'POST',
|
||||
@@ -451,9 +476,10 @@ export async function generateAIImage(genPrompt:string, currentChar:character, n
|
||||
|
||||
const startTime = Date.now()
|
||||
const timeout = db.comfyConfig.timeout * 1000
|
||||
while (!(item = (await (await fetch(createUrl('/history'), {
|
||||
while (!(item = (await (await fetchNative(createUrl('/history'), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
method: 'GET'})).json())[id])) {
|
||||
method: 'GET'
|
||||
})).json())[id])) {
|
||||
console.log("Checking /history...")
|
||||
if (Date.now() - startTime >= timeout) {
|
||||
alertError("Error: Image generation took longer than expected.");
|
||||
@@ -463,13 +489,14 @@ export async function generateAIImage(genPrompt:string, currentChar:character, n
|
||||
} // Check history until the generation is complete.
|
||||
const genImgInfo = Object.values(item.outputs).flatMap((output: any) => output.images)[0];
|
||||
|
||||
const imgResponse = await fetch(createUrl('/view', {
|
||||
const imgResponse = await fetchNative(createUrl('/view', {
|
||||
filename: genImgInfo.filename,
|
||||
subfolder: genImgInfo.subfolder,
|
||||
type: genImgInfo.type
|
||||
}), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
method: 'GET'})
|
||||
method: 'GET'
|
||||
})
|
||||
const img64 = Buffer.from(await imgResponse.arrayBuffer()).toString('base64')
|
||||
|
||||
if(returnSdData === 'inlay'){
|
||||
@@ -552,7 +579,6 @@ export async function generateAIImage(genPrompt:string, currentChar:character, n
|
||||
if(db.falModel === 'fal-ai/flux-pro'){
|
||||
delete body.enable_safety_checker
|
||||
}
|
||||
console.log(body)
|
||||
|
||||
const res = await globalFetch('https://fal.run/' + model, {
|
||||
headers: {
|
||||
@@ -563,8 +589,6 @@ export async function generateAIImage(genPrompt:string, currentChar:character, n
|
||||
body: body
|
||||
})
|
||||
|
||||
console.log(res)
|
||||
|
||||
if(!res.ok){
|
||||
alertError(JSON.stringify(res.data))
|
||||
return false
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getDatabase } from "./database.svelte"
|
||||
import { hubURL } from "../characterCards"
|
||||
import localforage from "localforage"
|
||||
import { alertLogin, alertNormalWait, alertStore, alertWait } from "../alert"
|
||||
import { forageStorage, getUnpargeables } from "../globalApi.svelte"
|
||||
import { AppendableBuffer, forageStorage, getUnpargeables } from "../globalApi.svelte"
|
||||
import { encodeRisuSaveLegacy } from "./risuSave"
|
||||
import { v4 } from "uuid"
|
||||
import { language } from "src/lang"
|
||||
@@ -87,7 +87,7 @@ export class AccountStorage{
|
||||
}
|
||||
return await getDaText()
|
||||
}
|
||||
async getItem(key:string):Promise<Buffer> {
|
||||
async getItem(key:string, callback?:(status:number) => void):Promise<Buffer> {
|
||||
this.checkAuth()
|
||||
if(key.startsWith('assets/')){
|
||||
const k:ArrayBuffer = await localforage.getItem(key)
|
||||
@@ -131,11 +131,38 @@ export class AccountStorage{
|
||||
if(da.status === 204){
|
||||
return null
|
||||
}
|
||||
const ab = await da.arrayBuffer()
|
||||
if(key.startsWith('assets/')){
|
||||
const ab = await da.arrayBuffer()
|
||||
await localforage.setItem(key, ab)
|
||||
return Buffer.from(ab)
|
||||
}
|
||||
return Buffer.from(ab)
|
||||
if(!callback){
|
||||
const ab = await da.arrayBuffer()
|
||||
return Buffer.from(ab)
|
||||
}
|
||||
const size = parseInt(da.headers.get('x-body-size'))
|
||||
const appendable = new Uint8Array(size)
|
||||
const reader = da.body.getReader()
|
||||
|
||||
//log all headers
|
||||
console.log('logging headers')
|
||||
for(const [key, value] of da.headers.entries()){
|
||||
console.log(key, value)
|
||||
}
|
||||
|
||||
let i = 0
|
||||
while(true){
|
||||
const {done, value} = await reader.read()
|
||||
if(done){
|
||||
break
|
||||
}
|
||||
console.log(value, size)
|
||||
appendable.set(value, i)
|
||||
i += value.length
|
||||
callback(i/size)
|
||||
}
|
||||
|
||||
return Buffer.from(appendable)
|
||||
}
|
||||
async keys():Promise<string[]>{
|
||||
let db = getDatabase()
|
||||
|
||||
@@ -117,7 +117,7 @@ export class AutoStorage{
|
||||
return false
|
||||
}
|
||||
|
||||
private async Init(){
|
||||
async Init(){
|
||||
if(!this.realStorage){
|
||||
if(localStorage.getItem('accountst') === 'able'){
|
||||
this.realStorage = new AccountStorage()
|
||||
|
||||
@@ -5,14 +5,14 @@ import type { RisuPlugin } from '../plugins/plugins';
|
||||
import type {triggerscript as triggerscriptMain} from '../process/triggers';
|
||||
import { downloadFile, saveAsset as saveImageGlobal } from '../globalApi.svelte';
|
||||
import { defaultAutoSuggestPrompt, defaultJailbreak, defaultMainPrompt } from './defaultPrompts';
|
||||
import { alertNormal, alertSelect } from '../alert';
|
||||
import { alertError, alertNormal, alertSelect } from '../alert';
|
||||
import type { NAISettings } from '../process/models/nai';
|
||||
import { prebuiltNAIpresets, prebuiltPresets } from '../process/templates/templates';
|
||||
import { defaultColorScheme, type ColorScheme } from '../gui/colorscheme';
|
||||
import type { PromptItem, PromptSettings } from '../process/prompt';
|
||||
import type { OobaChatCompletionRequestParams } from '../model/ooba';
|
||||
|
||||
export let appVer = "144.1.0"
|
||||
export let appVer = "145.0.1"
|
||||
export let webAppSubVer = ''
|
||||
|
||||
|
||||
@@ -356,6 +356,7 @@ export function setDatabase(data:Database){
|
||||
data.huggingfaceKey ??= ''
|
||||
data.fishSpeechKey ??= ''
|
||||
data.statistics ??= {}
|
||||
data.presetRegex ??= []
|
||||
data.reverseProxyOobaArgs ??= {
|
||||
mode: 'instruct'
|
||||
}
|
||||
@@ -465,6 +466,7 @@ export function setDatabase(data:Database){
|
||||
data.customFlags ??= []
|
||||
data.enableCustomFlags ??= false
|
||||
data.assetMaxDifference ??= 4
|
||||
data.showSavingIcon ??= false
|
||||
changeLanguage(data.language)
|
||||
setDatabaseLite(data)
|
||||
}
|
||||
@@ -862,6 +864,8 @@ export interface Database{
|
||||
assetMaxDifference:number
|
||||
menuSideBar:boolean
|
||||
pluginV2: RisuPlugin[]
|
||||
showSavingIcon:boolean
|
||||
presetRegex: customscript[]
|
||||
}
|
||||
|
||||
interface SeparateParameters{
|
||||
@@ -1183,6 +1187,8 @@ export interface botPreset{
|
||||
openAIPrediction?: string
|
||||
enableCustomFlags?: boolean
|
||||
customFlags?: LLMFlags[]
|
||||
image?:string
|
||||
regex?:customscript[]
|
||||
}
|
||||
|
||||
|
||||
@@ -1483,6 +1489,8 @@ export function saveCurrentPreset(){
|
||||
systemRoleReplacement: db.systemRoleReplacement,
|
||||
customFlags: safeStructuredClone(db.customFlags),
|
||||
enableCustomFlags: db.enableCustomFlags,
|
||||
regex: db.presetRegex,
|
||||
image: pres?.[db.botPresetsId]?.image ?? '',
|
||||
}
|
||||
db.botPresets = pres
|
||||
setDatabase(db)
|
||||
@@ -1590,6 +1598,7 @@ export function setPreset(db:Database, newPres: botPreset){
|
||||
db.systemRoleReplacement = newPres.systemRoleReplacement ?? 'user'
|
||||
db.customFlags = safeStructuredClone(newPres.customFlags) ?? []
|
||||
db.enableCustomFlags = newPres.enableCustomFlags ?? false
|
||||
db.presetRegex = newPres.regex ?? []
|
||||
return db
|
||||
}
|
||||
|
||||
@@ -1615,6 +1624,12 @@ export async function downloadPreset(id:number, type:'json'|'risupreset'|'return
|
||||
pres.proxyKey = ''
|
||||
pres.textgenWebUIStreamURL= ''
|
||||
pres.textgenWebUIBlockingURL= ''
|
||||
|
||||
if((pres.image || pres.regex?.length > 0) && type !== 'return'){
|
||||
alertError("Preset with image or regexes cannot be exported for now. use RisuRealm to share the preset.")
|
||||
return
|
||||
}
|
||||
|
||||
if(type === 'json'){
|
||||
downloadFile(pres.name + "_preset.json", Buffer.from(JSON.stringify(pres, null, 2)))
|
||||
}
|
||||
@@ -1627,15 +1642,16 @@ export async function downloadPreset(id:number, type:'json'|'risupreset'|'return
|
||||
'risupreset'
|
||||
)
|
||||
}))
|
||||
|
||||
|
||||
const buf2 = await encodeRPack(buf)
|
||||
|
||||
if(type === 'risupreset'){
|
||||
const buf2 = await encodeRPack(buf)
|
||||
downloadFile(pres.name + "_preset.risup", buf2)
|
||||
}
|
||||
else{
|
||||
return {
|
||||
data: pres,
|
||||
buf
|
||||
buf: buf2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,10 @@ export const DBState = $state({
|
||||
db: {} as any as Database
|
||||
});
|
||||
|
||||
export const LoadingStatusState = $state({
|
||||
text: '',
|
||||
})
|
||||
|
||||
export const disableHighlight = writable(true)
|
||||
|
||||
ReloadGUIPointer.subscribe(() => {
|
||||
@@ -111,7 +115,6 @@ $effect.root(() => {
|
||||
DBState?.db?.characters?.[selIdState.selId]?.chats?.[DBState?.db?.characters?.[selIdState.selId]?.chatPage]?.modules?.length
|
||||
DBState?.db?.characters?.[selIdState.selId]?.hideChatIcon
|
||||
DBState?.db?.characters?.[selIdState.selId]?.backgroundHTML
|
||||
DBState?.db?.moduleIntergration
|
||||
moduleUpdate()
|
||||
})
|
||||
})
|
||||
@@ -7,6 +7,8 @@ import { risuChatParser } from "./parser.svelte";
|
||||
import { tokenizeGGUFModel } from "./process/models/local";
|
||||
import { globalFetch } from "./globalApi.svelte";
|
||||
import { getModelInfo, LLMTokenizer } from "./model/modellist";
|
||||
import { pluginV2 } from "./plugins/plugins";
|
||||
import type { GemmaTokenizer } from "@huggingface/transformers";
|
||||
|
||||
|
||||
export const tokenizerList = [
|
||||
@@ -38,15 +40,46 @@ export async function encode(data:string):Promise<(number[]|Uint32Array|Int32Arr
|
||||
case 'llama3':
|
||||
return await tokenizeWebTokenizers(data, 'llama')
|
||||
case 'gemma':
|
||||
return await tokenizeWebTokenizers(data, 'gemma')
|
||||
return await gemmaTokenize(data)
|
||||
case 'cohere':
|
||||
return await tokenizeWebTokenizers(data, 'cohere')
|
||||
default:
|
||||
return await tikJS(data, 'o200k_base')
|
||||
}
|
||||
}
|
||||
|
||||
const modelInfo = getModelInfo(db.aiModel)
|
||||
|
||||
if(db.aiModel === 'custom' && pluginV2.providerOptions.get(db.currentPluginProvider)?.tokenizer){
|
||||
const tokenizer = pluginV2.providerOptions.get(db.currentPluginProvider)?.tokenizer
|
||||
switch(tokenizer){
|
||||
case 'mistral':
|
||||
return await tokenizeWebTokenizers(data, 'mistral')
|
||||
case 'llama':
|
||||
return await tokenizeWebTokenizers(data, 'llama')
|
||||
case 'novelai':
|
||||
return await tokenizeWebTokenizers(data, 'novelai')
|
||||
case 'claude':
|
||||
return await tokenizeWebTokenizers(data, 'claude')
|
||||
case 'novellist':
|
||||
return await tokenizeWebTokenizers(data, 'novellist')
|
||||
case 'llama3':
|
||||
return await tokenizeWebTokenizers(data, 'llama')
|
||||
case 'gemma':
|
||||
return await gemmaTokenize(data)
|
||||
case 'cohere':
|
||||
return await tokenizeWebTokenizers(data, 'cohere')
|
||||
case 'o200k_base':
|
||||
return await tikJS(data, 'o200k_base')
|
||||
case 'cl100k_base':
|
||||
return await tikJS(data, 'cl100k_base')
|
||||
case 'custom':
|
||||
return await pluginV2.providerOptions.get(db.currentPluginProvider)?.tokenizerFunc?.(data) ?? [0]
|
||||
default:
|
||||
return await tikJS(data, 'o200k_base')
|
||||
}
|
||||
}
|
||||
|
||||
if(modelInfo.tokenizer === LLMTokenizer.NovelList){
|
||||
const nv= await tokenizeWebTokenizers(data, 'novellist')
|
||||
return nv
|
||||
@@ -73,7 +106,7 @@ export async function encode(data:string):Promise<(number[]|Uint32Array|Int32Arr
|
||||
return await tokenizeGoogleCloud(data)
|
||||
}
|
||||
if(modelInfo.tokenizer === LLMTokenizer.Gemma || modelInfo.tokenizer === LLMTokenizer.GoogleCloud){
|
||||
return await tokenizeWebTokenizers(data, 'gemma')
|
||||
return await gemmaTokenize(data)
|
||||
}
|
||||
if(modelInfo.tokenizer === LLMTokenizer.Cohere){
|
||||
return await tokenizeWebTokenizers(data, 'cohere')
|
||||
@@ -125,6 +158,17 @@ async function tokenizeGoogleCloud(text:string) {
|
||||
return new Uint32Array(count)
|
||||
}
|
||||
|
||||
let gemmaTokenizer:GemmaTokenizer = null
|
||||
async function gemmaTokenize(text:string) {
|
||||
if(!gemmaTokenizer){
|
||||
const {GemmaTokenizer} = await import('@huggingface/transformers')
|
||||
gemmaTokenizer = new GemmaTokenizer(
|
||||
await (await fetch("/token/llama/llama3.json")
|
||||
).json(), {})
|
||||
}
|
||||
return gemmaTokenizer.encode(text)
|
||||
}
|
||||
|
||||
async function tikJS(text:string, model='cl100k_base') {
|
||||
if(!tikParser || lastTikModel !== model){
|
||||
if(model === 'cl100k_base'){
|
||||
|
||||
Reference in New Issue
Block a user