feat: add lua triggers
This commit is contained in:
14
pnpm-lock.yaml
generated
14
pnpm-lock.yaml
generated
@@ -184,7 +184,7 @@ importers:
|
||||
specifier: ^5.6.0
|
||||
version: 5.6.0
|
||||
'@sveltejs/vite-plugin-svelte':
|
||||
specifier: ^3.0.1
|
||||
specifier: 3.0.1
|
||||
version: 3.0.1(svelte@4.2.8)(vite@5.2.12(@types/node@18.19.7))
|
||||
'@swc/core':
|
||||
specifier: 1.5.7
|
||||
@@ -250,13 +250,13 @@ importers:
|
||||
specifier: ^8.4.33
|
||||
version: 8.4.33
|
||||
svelte:
|
||||
specifier: ^4.2.8
|
||||
specifier: 4.2.8
|
||||
version: 4.2.8
|
||||
svelte-check:
|
||||
specifier: ^3.6.3
|
||||
specifier: 3.6.3
|
||||
version: 3.6.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))(postcss@8.4.33)(svelte@4.2.8)
|
||||
svelte-preprocess:
|
||||
specifier: ^5.1.3
|
||||
specifier: 5.1.3
|
||||
version: 5.1.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))(postcss@8.4.33)(svelte@4.2.8)(typescript@5.3.3)
|
||||
tailwindcss:
|
||||
specifier: ^3.4.1
|
||||
@@ -268,13 +268,13 @@ importers:
|
||||
specifier: ^5.3.3
|
||||
version: 5.3.3
|
||||
vite:
|
||||
specifier: ^5.2.12
|
||||
specifier: 5.2.12
|
||||
version: 5.2.12(@types/node@18.19.7)
|
||||
vite-plugin-top-level-await:
|
||||
specifier: ^1.4.1
|
||||
specifier: 1.4.1
|
||||
version: 1.4.1(rollup@3.29.4)(vite@5.2.12(@types/node@18.19.7))
|
||||
vite-plugin-wasm:
|
||||
specifier: ^3.3.0
|
||||
specifier: 3.3.0
|
||||
version: 3.3.0(vite@5.2.12(@types/node@18.19.7))
|
||||
|
||||
packages:
|
||||
|
||||
@@ -664,6 +664,6 @@ export const languageEnglish = {
|
||||
includePersonaName: "Include Persona Name",
|
||||
hidePersonaName: "Hide Persona Name",
|
||||
triggerSwitchWarn: "If you change the trigger type, current triggers will be lost. do you want to continue?",
|
||||
codeMode: "Code Mode",
|
||||
blockMode: "Block Mode",
|
||||
codeMode: "Code",
|
||||
blockMode: "Block",
|
||||
}
|
||||
@@ -581,19 +581,25 @@
|
||||
|
||||
<span class="text-textcolor mt-4">{language.triggerScript} <Help key="triggerScript"/></span>
|
||||
<div class="flex items-start mt-2 gap-2">
|
||||
<button class="bg-bgcolor py-2 rounded-lg px-4" class:ring-1={currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type !== 'triggercode'} on:click|stopPropagation={async () => {
|
||||
if(currentChar.type === 'character' && currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggercode'){
|
||||
const codeTrigger = currentChar.data?.triggerscript?.[0]?.effect?.[0]?.code
|
||||
if(codeTrigger){
|
||||
const t = await alertConfirm(language.triggerSwitchWarn)
|
||||
if(!t){
|
||||
return
|
||||
<button class="bg-bgcolor py-1 rounded-md text-sm px-2" class:ring-1={
|
||||
currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type !== 'triggercode' &&
|
||||
currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type !== 'triggerlua'
|
||||
} on:click|stopPropagation={async () => {
|
||||
if(currentChar.type === 'character'){
|
||||
const codeType = currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type
|
||||
if(codeType === 'triggercode' || codeType === 'triggerlua'){
|
||||
const codeTrigger = currentChar.data?.triggerscript?.[0]?.effect?.[0]?.code
|
||||
if(codeTrigger){
|
||||
const t = await alertConfirm(language.triggerSwitchWarn)
|
||||
if(!t){
|
||||
return
|
||||
}
|
||||
}
|
||||
currentChar.data.triggerscript = []
|
||||
}
|
||||
}
|
||||
currentChar.data.triggerscript = []
|
||||
}
|
||||
}}>{language.blockMode}</button>
|
||||
<button class="bg-bgcolor py-2 rounded-lg px-4" class:ring-1={currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggercode'} on:click|stopPropagation={async () => {
|
||||
<button class="bg-bgcolor py-1 rounded-md text-sm px-2" class:ring-1={currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggercode'} on:click|stopPropagation={async () => {
|
||||
if(currentChar.type === 'character' && currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type !== 'triggercode'){
|
||||
if(currentChar.data?.triggerscript && currentChar.data?.triggerscript.length > 0){
|
||||
const t = await alertConfirm(language.triggerSwitchWarn)
|
||||
@@ -612,8 +618,27 @@
|
||||
}]
|
||||
}
|
||||
}}>{language.codeMode}</button>
|
||||
<button class="bg-bgcolor py-1 rounded-md text-sm px-2" class:ring-1={currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggerlua'} on:click|stopPropagation={async () => {
|
||||
if(currentChar.type === 'character' && currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type !== 'triggerlua'){
|
||||
if(currentChar.data?.triggerscript && currentChar.data?.triggerscript.length > 0){
|
||||
const t = await alertConfirm(language.triggerSwitchWarn)
|
||||
if(!t){
|
||||
return
|
||||
}
|
||||
}
|
||||
currentChar.data.triggerscript = [{
|
||||
comment: "",
|
||||
type: "start",
|
||||
conditions: [],
|
||||
effect: [{
|
||||
type: "triggerlua",
|
||||
code: ""
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}}>Lua</button>
|
||||
</div>
|
||||
{#if currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggercode'}
|
||||
{#if currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggercode' || currentChar.data?.triggerscript?.[0]?.effect?.[0]?.type === 'triggerlua'}
|
||||
<TextAreaInput highlight margin="both" autocomplete="off" bind:value={currentChar.data.triggerscript[0].effect[0].code}></TextAreaInput>
|
||||
{:else}
|
||||
<TriggerList bind:value={currentChar.data.triggerscript} lowLevelAble={currentChar.data.lowLevelAccess} />
|
||||
|
||||
15
src/ts/process/lua.ts
Normal file
15
src/ts/process/lua.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { LuaEngine } from "wasmoon";
|
||||
import type { character, groupChat } from "../storage/database";
|
||||
import { risuChatParser } from "../parser";
|
||||
|
||||
export let LuaSafeIds = new Set<string>()
|
||||
|
||||
export function registerLuaProcess(engine:LuaEngine, char:character|groupChat) {
|
||||
engine.global.set('cbs', (code:string) => {
|
||||
const parsed = risuChatParser(code, {
|
||||
chara: char,
|
||||
})
|
||||
return parsed
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,6 +12,14 @@ import { HypaProcesser } from "./memory/hypamemory";
|
||||
import { requestChatData } from "./request";
|
||||
import { generateAIImage } from "./stableDiff";
|
||||
import { writeInlayImage } from "./files/image";
|
||||
import { LuaEngine, LuaFactory } from "wasmoon";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
let luaFactory:LuaFactory
|
||||
let luaEngine:LuaEngine
|
||||
let lastCode = ''
|
||||
let LuaSafeIds = new Set<string>()
|
||||
let LuaLowLevelIds = new Set<string>()
|
||||
|
||||
export interface triggerscript{
|
||||
comment: string;
|
||||
@@ -33,7 +41,7 @@ export type triggerConditionsVar = {
|
||||
}
|
||||
|
||||
export type triggerCode = {
|
||||
type: 'triggercode',
|
||||
type: 'triggercode'|'triggerlua',
|
||||
code: string
|
||||
}
|
||||
|
||||
@@ -202,12 +210,15 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{
|
||||
|
||||
|
||||
for(const trigger of triggers){
|
||||
if(arg.manualName){
|
||||
if(trigger.effect[0]?.type === 'triggercode' || trigger.effect[0]?.type === 'triggerlua'){
|
||||
//
|
||||
}
|
||||
else if(arg.manualName){
|
||||
if(trigger.comment !== arg.manualName){
|
||||
continue
|
||||
}
|
||||
}
|
||||
else if(mode !== trigger.type && trigger.effect[0]?.type !== 'triggercode'){
|
||||
else if(mode !== trigger.type){
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -535,7 +546,297 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{
|
||||
stopSending = true
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'triggerlua':{
|
||||
if(!luaEngine || lastCode !== effect.code){
|
||||
if(luaEngine){
|
||||
luaEngine.global.close()
|
||||
}
|
||||
luaFactory = new LuaFactory()
|
||||
luaEngine = await luaFactory.createEngine()
|
||||
luaEngine.global.set('cbs', (code:string) => {
|
||||
const parsed = risuChatParser(code, {
|
||||
chara: char,
|
||||
})
|
||||
return parsed
|
||||
})
|
||||
luaEngine.global.set('setChatVar', (id:string,key:string, value:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
setVar(key, value)
|
||||
})
|
||||
luaEngine.global.set('getChatVar', (id:string,key:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
return getVar(key)
|
||||
})
|
||||
luaEngine.global.set('stopChat', (id:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
stopSending = true
|
||||
})
|
||||
luaEngine.global.set('alertError', (id:string, value:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
alertError(value)
|
||||
})
|
||||
luaEngine.global.set('alertNormal', (id:string, value:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
alertNormal(value)
|
||||
})
|
||||
luaEngine.global.set('alertInput', (id:string, value:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
return alertInput(value)
|
||||
})
|
||||
luaEngine.global.set('setChat', (id:string, index:number, value:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
const message = chat.message?.at(index)
|
||||
if(message){
|
||||
message.data = value
|
||||
}
|
||||
CurrentChat.set(chat)
|
||||
})
|
||||
luaEngine.global.set('setChatRole', (id:string, index:number, value:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
const message = chat.message?.at(index)
|
||||
if(message){
|
||||
message.role = value === 'user' ? 'user' : 'char'
|
||||
}
|
||||
CurrentChat.set(chat)
|
||||
})
|
||||
luaEngine.global.set('cutChat', (id:string, start:number, end:number) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
chat.message = chat.message.slice(start,end)
|
||||
CurrentChat.set(chat)
|
||||
})
|
||||
luaEngine.global.set('addChat', (id:string, role:string, value:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
let roleData:'user'|'char' = role === 'user' ? 'user' : 'char'
|
||||
chat.message.push({role: roleData, data: value})
|
||||
CurrentChat.set(chat)
|
||||
})
|
||||
luaEngine.global.set('insertChat', (id:string, index:number, role:string, value:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
let roleData:'user'|'char' = role === 'user' ? 'user' : 'char'
|
||||
chat.message.splice(index, 0, {role: roleData, data: value})
|
||||
CurrentChat.set(chat)
|
||||
})
|
||||
luaEngine.global.set('removeChat', (id:string, index:number) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
chat.message.splice(index, 1)
|
||||
CurrentChat.set(chat)
|
||||
})
|
||||
luaEngine.global.set('getChatLength', (id:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
return chat.message.length
|
||||
})
|
||||
luaEngine.global.set('getFullChat', (id:string) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
return chat.message.map((v) => {
|
||||
return {
|
||||
role: v.role,
|
||||
data: v.data
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
luaEngine.global.set('setFullChat', (id:string, value:any[]) => {
|
||||
if(!LuaSafeIds.has(id)){
|
||||
return
|
||||
}
|
||||
chat.message = value.map((v) => {
|
||||
return {
|
||||
role: v.role,
|
||||
data: v.data
|
||||
}
|
||||
})
|
||||
CurrentChat.set(chat)
|
||||
})
|
||||
|
||||
//Low Level Access
|
||||
luaEngine.global.set('similarity', (id:string, source:string, value:string[]) => {
|
||||
if(!LuaLowLevelIds.has(id)){
|
||||
return
|
||||
}
|
||||
const processer = new HypaProcesser('MiniLM')
|
||||
processer.addText(value)
|
||||
return processer.similaritySearch(source)
|
||||
})
|
||||
|
||||
luaEngine.global.set('generateImage', async (id:string, value:string, negValue:string = '') => {
|
||||
if(!LuaLowLevelIds.has(id)){
|
||||
return
|
||||
}
|
||||
const gen = await generateAIImage(value, char, negValue, 'inlay')
|
||||
if(!gen){
|
||||
return 'Error: Image generation failed'
|
||||
}
|
||||
const imgHTML = new Image()
|
||||
imgHTML.src = gen
|
||||
const inlay = await writeInlayImage(imgHTML)
|
||||
return `{{inlay::${inlay}}}`
|
||||
})
|
||||
|
||||
luaEngine.global.set('LLM', async (id:string, prompt:{
|
||||
role: string,
|
||||
content: string
|
||||
}[]) => {
|
||||
if(!LuaLowLevelIds.has(id)){
|
||||
return
|
||||
}
|
||||
let promptbody:OpenAIChat[] = prompt.map((dict) => {
|
||||
let role:'system'|'user'|'assistant' = 'assistant'
|
||||
switch(dict['role']){
|
||||
case 'system':
|
||||
case 'sys':
|
||||
role = 'system'
|
||||
break
|
||||
case 'user':
|
||||
role = 'user'
|
||||
break
|
||||
case 'assistant':
|
||||
case 'bot':
|
||||
case 'char':{
|
||||
role = 'assistant'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: dict['content'] ?? '',
|
||||
role: role,
|
||||
}
|
||||
})
|
||||
const result = await requestChatData({
|
||||
formated: promptbody,
|
||||
bias: {},
|
||||
useStreaming: false,
|
||||
noMultiGen: true,
|
||||
}, 'model')
|
||||
|
||||
if(result.type === 'fail'){
|
||||
return {
|
||||
success: false,
|
||||
result: 'Error: ' + result.result
|
||||
}
|
||||
}
|
||||
|
||||
if(result.type === 'streaming' || result.type === 'multiline'){
|
||||
return {
|
||||
success: false,
|
||||
result: result.result
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: result.result
|
||||
}
|
||||
})
|
||||
|
||||
luaEngine.global.set('simpleLLM', async (id:string, prompt:string) => {
|
||||
const result = await requestChatData({
|
||||
formated: [{
|
||||
role: 'user',
|
||||
content: prompt
|
||||
}],
|
||||
bias: {},
|
||||
useStreaming: false,
|
||||
noMultiGen: true,
|
||||
}, 'model')
|
||||
|
||||
if(result.type === 'fail'){
|
||||
return {
|
||||
success: false,
|
||||
result: 'Error: ' + result.result
|
||||
}
|
||||
}
|
||||
|
||||
if(result.type === 'streaming' || result.type === 'multiline'){
|
||||
return {
|
||||
success: false,
|
||||
result: result.result
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: result.result
|
||||
}
|
||||
})
|
||||
|
||||
await luaEngine.doString(effect.code)
|
||||
lastCode = effect.code
|
||||
}
|
||||
let accessKey = v4()
|
||||
LuaSafeIds.add(accessKey)
|
||||
if(trigger.lowLevelAccess){
|
||||
LuaLowLevelIds.add(v4())
|
||||
}
|
||||
try {
|
||||
let res:any
|
||||
switch(mode){
|
||||
case 'input':{
|
||||
const func = luaEngine.global.get('onInput')
|
||||
if(func){
|
||||
res = await func(accessKey)
|
||||
}
|
||||
}
|
||||
case 'output':{
|
||||
const func = luaEngine.global.get('onOutput')
|
||||
if(func){
|
||||
res = await func(accessKey)
|
||||
}
|
||||
}
|
||||
case 'start':{
|
||||
const func = luaEngine.global.get('onStart')
|
||||
if(func){
|
||||
res = await func(accessKey)
|
||||
}
|
||||
}
|
||||
case 'manual':{
|
||||
const func = luaEngine.global.get(arg.manualName)
|
||||
if(func){
|
||||
res = await func(accessKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
if(res === false){
|
||||
stopSending = true
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
alertError('Lua Error: ' + error)
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
LuaSafeIds.delete(accessKey)
|
||||
LuaLowLevelIds.delete(accessKey)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user