Add python work - WIP

This commit is contained in:
Kwaroran
2025-06-02 22:54:06 +09:00
parent 9a44084e96
commit dd97137bf9
8 changed files with 335 additions and 171 deletions

View File

@@ -78,6 +78,7 @@
"png-chunk-text": "^1.0.0", "png-chunk-text": "^1.0.0",
"png-chunks-encode": "^1.0.0", "png-chunks-encode": "^1.0.0",
"png-chunks-extract": "^1.0.0", "png-chunks-extract": "^1.0.0",
"pyodide": "^0.27.6",
"rfdc": "^1.4.1", "rfdc": "^1.4.1",
"rollup": "^3.29.4", "rollup": "^3.29.4",
"showdown": "^2.1.0", "showdown": "^2.1.0",

28
pnpm-lock.yaml generated
View File

@@ -194,6 +194,9 @@ importers:
png-chunks-extract: png-chunks-extract:
specifier: ^1.0.0 specifier: ^1.0.0
version: 1.0.0 version: 1.0.0
pyodide:
specifier: ^0.27.6
version: 0.27.6
rfdc: rfdc:
specifier: ^1.4.1 specifier: ^1.4.1
version: 1.4.1 version: 1.4.1
@@ -3112,6 +3115,10 @@ packages:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
engines: {node: '>=6'} engines: {node: '>=6'}
pyodide@0.27.6:
resolution: {integrity: sha512-ahiSHHs6iFKl2f8aO1wALINAlMNDLAtb44xCI87GQyH2tLDk8F8VWip3u1ZNIyglGSCYAOSFzWKwS1f9gBFVdg==}
engines: {node: '>=18.0.0'}
q@1.5.1: q@1.5.1:
resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==}
engines: {node: '>=0.6.0', teleport: '>=0.2.0'} engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
@@ -3832,6 +3839,18 @@ packages:
wrappy@1.0.2: wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
ws@8.18.2:
resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
xcode@3.0.1: xcode@3.0.1:
resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==} resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@@ -6702,6 +6721,13 @@ snapshots:
punycode.js@2.3.1: {} punycode.js@2.3.1: {}
pyodide@0.27.6:
dependencies:
ws: 8.18.2
transitivePeerDependencies:
- bufferutil
- utf-8-validate
q@1.5.1: {} q@1.5.1: {}
qs@6.11.0: qs@6.11.0:
@@ -7465,6 +7491,8 @@ snapshots:
wrappy@1.0.2: {} wrappy@1.0.2: {}
ws@8.18.2: {}
xcode@3.0.1: xcode@3.0.1:
dependencies: dependencies:
simple-plist: 1.3.1 simple-plist: 1.3.1

View File

@@ -1,7 +1,7 @@
import { runTrigger } from "./process/triggers"; import { runTrigger } from "./process/triggers";
import { sleep } from "./util"; import { sleep } from "./util";
import { getCurrentCharacter, getCurrentChat, setCurrentChat } from "./storage/database.svelte"; import { getCurrentCharacter, getCurrentChat, setCurrentChat } from "./storage/database.svelte";
import { runLuaButtonTrigger } from "./process/lua"; import { runLuaButtonTrigger } from "./process/scriptings";
import { globalFetch } from "./globalApi.svelte"; import { globalFetch } from "./globalApi.svelte";
let bgmElement:HTMLAudioElement|null = null; let bgmElement:HTMLAudioElement|null = null;

View File

@@ -26,7 +26,7 @@ import { addRerolls } from "./prereroll";
import { runImageEmbedding } from "./transformers"; import { runImageEmbedding } from "./transformers";
import { hanuraiMemory } from "./memory/hanuraiMemory"; import { hanuraiMemory } from "./memory/hanuraiMemory";
import { hypaMemoryV2 } from "./memory/hypav2"; import { hypaMemoryV2 } from "./memory/hypav2";
import { runLuaEditTrigger } from "./lua"; import { runLuaEditTrigger } from "./scriptings";
import { getGlobalChatVar, parseChatML } from "../parser.svelte"; import { getGlobalChatVar, parseChatML } from "../parser.svelte";
import { getModelInfo, LLMFlags } from "../model/modellist"; import { getModelInfo, LLMFlags } from "../model/modellist";
import { hypaMemoryV3 } from "./memory/hypav3"; import { hypaMemoryV3 } from "./memory/hypav3";

105
src/ts/process/pyworker.ts Normal file
View File

@@ -0,0 +1,105 @@
//This is a web worker that runs Python code using Pyodide.
import { loadPyodide, version as pyodideVersion, type PyodideInterface } from "pyodide";
type InitMessage = {
type: "init";
id: string;
moduleFunctions: string[];
}
type FunctionResultMessage = {
type: "functionResult";
callId: string;
result: any;
}
type PythonMessage = {
type: 'python';
code: string;
}
type PyWorkerMessage = InitMessage | FunctionResultMessage | PythonMessage;
let py: PyodideInterface;
async function initPyodide() {
if(py){
return py;
}
py = await loadPyodide({
indexURL: `https://cdn.jsdelivr.net/pyodide/v${pyodideVersion}/full/`
});
return py;
}
self.onmessage = async (event:MessageEvent<PyWorkerMessage>) => {
await initPyodide()
const { type } = event.data;
switch(type) {
case 'init':{
const { id, moduleFunctions } = event.data as InitMessage;
let md: Record<string, any> = {};
for(const func of moduleFunctions) {
md[func] = (...args: any[]) => {
return new Promise((resolve, reject) => {
const callid = crypto.randomUUID();
self.postMessage({
type: "call",
function: func,
args,
callId: callid
});
const callee = (e: CustomEvent) => {
if(e.detail.callId === callid) {
globalThis.removeEventListener("x-function-call", callee);
resolve(e.detail.result);
}
}
globalThis.addEventListener("x-function-call", callee);
})
}
}
py.registerJsModule('risuai', md)
self.postMessage({
type: "init",
id,
version: pyodideVersion
});
break
}
case 'functionResult': {
const { callId, result } = event.data as FunctionResultMessage;
globalThis.dispatchEvent(new CustomEvent("x-function-call", {
detail: {
callId,
result
}
}));
break;
}
case 'python': {
const { code } = event.data as PythonMessage;
try {
const result = await py.runPythonAsync(code);
self.postMessage({
type: "pythonResult",
callId: crypto.randomUUID(),
result
});
} catch (error) {
console.error("Error executing Python code:", error);
self.postMessage({
type: "pythonResult",
callId: crypto.randomUUID(),
result: error
});
}
break;
}
}
};

View File

@@ -16,36 +16,48 @@ import { tokenize } from "../tokenizer";
import { fetchNative } from "../globalApi.svelte"; import { fetchNative } from "../globalApi.svelte";
import { loadLoreBookV3Prompt } from './lorebook.svelte'; import { loadLoreBookV3Prompt } from './lorebook.svelte';
import { getPersonaPrompt, getUserName } from '../util'; import { getPersonaPrompt, getUserName } from '../util';
let luaFactory:LuaFactory let luaFactory:LuaFactory
let LuaSafeIds = new Set<string>() let ScriptingSafeIds = new Set<string>()
let LuaEditDisplayIds = new Set<string>() let ScriptingEditDisplayIds = new Set<string>()
let LuaLowLevelIds = new Set<string>() let ScriptingLowLevelIds = new Set<string>()
let lastRequestResetTime = 0 let lastRequestResetTime = 0
let lastRequestsCount = 0 let lastRequestsCount = 0
interface LuaEngineState { interface BasicScriptingEngineState {
code?: string; code?: string;
engine?: LuaEngine;
mutex: Mutex; mutex: Mutex;
chat?: Chat; chat?: Chat;
setVar?: (key:string, value:string) => void, setVar?: (key:string, value:string) => void,
getVar?: (key:string) => string, getVar?: (key:string) => string,
} }
let LuaEngines = new Map<string, LuaEngineState>() interface LuaScriptingEngineState extends BasicScriptingEngineState {
let luaFactoryPromise: Promise<void> | null = null; engine?: LuaEngine;
let pendingEngineCreations = new Map<string, Promise<LuaEngineState>>(); type: 'lua';
}
export async function runLua(code:string, arg:{ interface PythonScriptingEngineState extends BasicScriptingEngineState {
type: 'py';
}
type ScriptingEngineState = LuaScriptingEngineState | PythonScriptingEngineState;
let ScriptingEngines = new Map<string, ScriptingEngineState>()
let luaFactoryPromise: Promise<void> | null = null;
let pendingEngineCreations = new Map<string, Promise<ScriptingEngineState>>();
export async function runScripted(code:string, arg:{
char?:character|groupChat|simpleCharacterArgument, char?:character|groupChat|simpleCharacterArgument,
chat?:Chat chat?:Chat
setVar?: (key:string, value:string) => void, setVar?: (key:string, value:string) => void,
getVar?: (key:string) => string, getVar?: (key:string) => string,
lowLevelAccess?: boolean, lowLevelAccess?: boolean,
mode?: string, mode?: string,
data?: any data?: any,
type?: 'lua'|'py'
}){ }){
const type: 'lua'|'py' = arg.type ?? 'lua'
const char = arg.char ?? getCurrentCharacter() const char = arg.char ?? getCurrentCharacter()
const setVar = arg.setVar ?? setChatVar const setVar = arg.setVar ?? setChatVar
const getVar = arg.getVar ?? getChatVar const getVar = arg.getVar ?? getChatVar
@@ -55,63 +67,76 @@ export async function runLua(code:string, arg:{
let stopSending = false let stopSending = false
let lowLevelAccess = arg.lowLevelAccess ?? false let lowLevelAccess = arg.lowLevelAccess ?? false
await ensureLuaFactory() if(type === 'lua'){
let luaEngineState = await getOrCreateEngineState(mode); await ensureLuaFactory()
}
let ScriptingEngineState = await getOrCreateEngineState(mode, type);
return await luaEngineState.mutex.runExclusive(async () => { return await ScriptingEngineState.mutex.runExclusive(async () => {
luaEngineState.chat = chat ScriptingEngineState.chat = chat
luaEngineState.setVar = setVar ScriptingEngineState.setVar = setVar
luaEngineState.getVar = getVar ScriptingEngineState.getVar = getVar
if (code !== luaEngineState.code) { if (code !== ScriptingEngineState.code) {
luaEngineState.engine?.global.close() let declareAPI:(name: string, func:Function) => void
luaEngineState.code = code
luaEngineState.engine = await luaFactory.createEngine({injectObjects: true}) if(ScriptingEngineState.type === 'lua'){
const luaEngine = luaEngineState.engine ScriptingEngineState.engine?.global.close()
luaEngine.global.set('getChatVar', (id:string,key:string) => { ScriptingEngineState.code = code
return luaEngineState.getVar(key) ScriptingEngineState.engine = await luaFactory.createEngine({injectObjects: true})
const luaEngine = ScriptingEngineState.engine
declareAPI = (name: string, func: Function) => {
luaEngine.global.set(name, func)
}
}
if(ScriptingEngineState.type === 'py'){
}
declareAPI('getChatVar', (id:string,key:string) => {
return ScriptingEngineState.getVar(key)
}) })
luaEngine.global.set('setChatVar', (id:string,key:string, value:string) => { declareAPI('setChatVar', (id:string,key:string, value:string) => {
if(!LuaSafeIds.has(id) && !LuaEditDisplayIds.has(id)){ if(!ScriptingSafeIds.has(id) && !ScriptingEditDisplayIds.has(id)){
return return
} }
luaEngineState.setVar(key, value) ScriptingEngineState.setVar(key, value)
}) })
luaEngine.global.set('getGlobalVar', (id:string, key:string) => { declareAPI('getGlobalVar', (id:string, key:string) => {
return getGlobalChatVar(key) return getGlobalChatVar(key)
}) })
luaEngine.global.set('stopChat', (id:string) => { declareAPI('stopChat', (id:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
stopSending = true stopSending = true
}) })
luaEngine.global.set('alertError', (id:string, value:string) => { declareAPI('alertError', (id:string, value:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
alertError(value) alertError(value)
}) })
luaEngine.global.set('alertNormal', (id:string, value:string) => { declareAPI('alertNormal', (id:string, value:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
alertNormal(value) alertNormal(value)
}) })
luaEngine.global.set('alertInput', (id:string, value:string) => { declareAPI('alertInput', (id:string, value:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
return alertInput(value) return alertInput(value)
}) })
luaEngine.global.set('alertSelect', (id:string, value:string[]) => { declareAPI('alertSelect', (id:string, value:string[]) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
return alertSelect(value) return alertSelect(value)
}) })
luaEngine.global.set('getChatMain', (id:string, index:number) => { declareAPI('getChatMain', (id:string, index:number) => {
const chat = luaEngineState.chat.message.at(index) const chat = ScriptingEngineState.chat.message.at(index)
if(!chat){ if(!chat){
return JSON.stringify(null) return JSON.stringify(null)
} }
@@ -123,64 +148,64 @@ export async function runLua(code:string, arg:{
return JSON.stringify(data) return JSON.stringify(data)
}) })
luaEngine.global.set('setChat', (id:string, index:number, value:string) => { declareAPI('setChat', (id:string, index:number, value:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
const message = luaEngineState.chat.message?.at(index) const message = ScriptingEngineState.chat.message?.at(index)
if(message){ if(message){
message.data = value message.data = value
} }
}) })
luaEngine.global.set('setChatRole', (id:string, index:number, value:string) => { declareAPI('setChatRole', (id:string, index:number, value:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
const message = luaEngineState.chat.message?.at(index) const message = ScriptingEngineState.chat.message?.at(index)
if(message){ if(message){
message.role = value === 'user' ? 'user' : 'char' message.role = value === 'user' ? 'user' : 'char'
} }
}) })
luaEngine.global.set('cutChat', (id:string, start:number, end:number) => { declareAPI('cutChat', (id:string, start:number, end:number) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
luaEngineState.chat.message = luaEngineState.chat.message.slice(start,end) ScriptingEngineState.chat.message = ScriptingEngineState.chat.message.slice(start,end)
}) })
luaEngine.global.set('removeChat', (id:string, index:number) => { declareAPI('removeChat', (id:string, index:number) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
luaEngineState.chat.message.splice(index, 1) ScriptingEngineState.chat.message.splice(index, 1)
}) })
luaEngine.global.set('addChat', (id:string, role:string, value:string) => { declareAPI('addChat', (id:string, role:string, value:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
let roleData:'user'|'char' = role === 'user' ? 'user' : 'char' let roleData:'user'|'char' = role === 'user' ? 'user' : 'char'
luaEngineState.chat.message.push({role: roleData, data: value}) ScriptingEngineState.chat.message.push({role: roleData, data: value})
}) })
luaEngine.global.set('insertChat', (id:string, index:number, role:string, value:string) => { declareAPI('insertChat', (id:string, index:number, role:string, value:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
let roleData:'user'|'char' = role === 'user' ? 'user' : 'char' let roleData:'user'|'char' = role === 'user' ? 'user' : 'char'
luaEngineState.chat.message.splice(index, 0, {role: roleData, data: value}) ScriptingEngineState.chat.message.splice(index, 0, {role: roleData, data: value})
}) })
luaEngine.global.set('getTokens', async (id:string, value:string) => { declareAPI('getTokens', async (id:string, value:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
return await tokenize(value) return await tokenize(value)
}) })
luaEngine.global.set('getChatLength', (id:string) => { declareAPI('getChatLength', (id:string) => {
return luaEngineState.chat.message.length return ScriptingEngineState.chat.message.length
}) })
luaEngine.global.set('getFullChatMain', (id:string) => { declareAPI('getFullChatMain', (id:string) => {
const data = JSON.stringify(luaEngineState.chat.message.map((v) => { const data = JSON.stringify(ScriptingEngineState.chat.message.map((v) => {
return { return {
role: v.role, role: v.role,
data: v.data, data: v.data,
@@ -190,13 +215,13 @@ export async function runLua(code:string, arg:{
return data return data
}) })
luaEngine.global.set('setFullChatMain', (id:string, value:string) => { declareAPI('setFullChatMain', (id:string, value:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
const realValue = JSON.parse(value) const realValue = JSON.parse(value)
luaEngineState.chat.message = realValue.map((v) => { ScriptingEngineState.chat.message = realValue.map((v) => {
return { return {
role: v.role, role: v.role,
data: v.data data: v.data
@@ -204,20 +229,20 @@ export async function runLua(code:string, arg:{
}) })
}) })
luaEngine.global.set('logMain', (value:string) => { declareAPI('logMain', (value:string) => {
console.log(JSON.parse(value)) console.log(JSON.parse(value))
}) })
luaEngine.global.set('reloadDisplay', (id:string) => { declareAPI('reloadDisplay', (id:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) ReloadGUIPointer.set(get(ReloadGUIPointer) + 1)
}) })
//Low Level Access //Low Level Access
luaEngine.global.set('similarity', async (id:string, source:string, value:string[]) => { declareAPI('similarity', async (id:string, source:string, value:string[]) => {
if(!LuaLowLevelIds.has(id)){ if(!ScriptingLowLevelIds.has(id)){
return return
} }
const processer = new HypaProcesser() const processer = new HypaProcesser()
@@ -225,8 +250,8 @@ export async function runLua(code:string, arg:{
return await processer.similaritySearch(source) return await processer.similaritySearch(source)
}) })
luaEngine.global.set('request', async (id:string, url:string) => { declareAPI('request', async (id:string, url:string) => {
if(!LuaLowLevelIds.has(id)){ if(!ScriptingLowLevelIds.has(id)){
return return
} }
@@ -294,8 +319,8 @@ export async function runLua(code:string, arg:{
} }
}) })
luaEngine.global.set('generateImage', async (id:string, value:string, negValue:string = '') => { declareAPI('generateImage', async (id:string, value:string, negValue:string = '') => {
if(!LuaLowLevelIds.has(id)){ if(!ScriptingLowLevelIds.has(id)){
return return
} }
const gen = await generateAIImage(value, char as character, negValue, 'inlay') const gen = await generateAIImage(value, char as character, negValue, 'inlay')
@@ -308,16 +333,16 @@ export async function runLua(code:string, arg:{
return `{{inlay::${inlay}}}` return `{{inlay::${inlay}}}`
}) })
luaEngine.global.set('hash', async (id:string, value:string) => { declareAPI('hash', async (id:string, value:string) => {
return await hasher(new TextEncoder().encode(value)) return await hasher(new TextEncoder().encode(value))
}) })
luaEngine.global.set('LLMMain', async (id:string, promptStr:string) => { declareAPI('LLMMain', async (id:string, promptStr:string) => {
let prompt:{ let prompt:{
role: string, role: string,
content: string content: string
}[] = JSON.parse(promptStr) }[] = JSON.parse(promptStr)
if(!LuaLowLevelIds.has(id)){ if(!ScriptingLowLevelIds.has(id)){
return return
} }
let promptbody:OpenAIChat[] = prompt.map((dict) => { let promptbody:OpenAIChat[] = prompt.map((dict) => {
@@ -370,8 +395,8 @@ export async function runLua(code:string, arg:{
}) })
}) })
luaEngine.global.set('simpleLLM', async (id:string, prompt:string) => { declareAPI('simpleLLM', async (id:string, prompt:string) => {
if(!LuaLowLevelIds.has(id)){ if(!ScriptingLowLevelIds.has(id)){
return return
} }
const result = await requestChatData({ const result = await requestChatData({
@@ -404,15 +429,15 @@ export async function runLua(code:string, arg:{
} }
}) })
luaEngine.global.set('getName', async (id:string) => { declareAPI('getName', async (id:string) => {
const db = getDatabase() const db = getDatabase()
const selectedChar = get(selectedCharID) const selectedChar = get(selectedCharID)
const char = db.characters[selectedChar] const char = db.characters[selectedChar]
return char.name return char.name
}) })
luaEngine.global.set('setName', async (id:string, name:string) => { declareAPI('setName', async (id:string, name:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
const db = getDatabase() const db = getDatabase()
@@ -424,8 +449,8 @@ export async function runLua(code:string, arg:{
setDatabase(db) setDatabase(db)
}) })
luaEngine.global.set('getDescription', async (id:string) => { declareAPI('getDescription', async (id:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
const db = getDatabase() const db = getDatabase()
@@ -437,8 +462,8 @@ export async function runLua(code:string, arg:{
return char.desc return char.desc
}) })
luaEngine.global.set('setDescription', async (id:string, desc:string) => { declareAPI('setDescription', async (id:string, desc:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
const db = getDatabase() const db = getDatabase()
@@ -455,15 +480,15 @@ export async function runLua(code:string, arg:{
setDatabase(db) setDatabase(db)
}) })
luaEngine.global.set('getCharacterFirstMessage', async (id:string) => { declareAPI('getCharacterFirstMessage', async (id:string) => {
const db = getDatabase() const db = getDatabase()
const selectedChar = get(selectedCharID) const selectedChar = get(selectedCharID)
const char = db.characters[selectedChar] const char = db.characters[selectedChar]
return char.firstMessage return char.firstMessage
}) })
luaEngine.global.set('setCharacterFirstMessage', async (id:string, data:string) => { declareAPI('setCharacterFirstMessage', async (id:string, data:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
const db = getDatabase() const db = getDatabase()
@@ -478,11 +503,11 @@ export async function runLua(code:string, arg:{
return true return true
}) })
luaEngine.global.set('getPersonaName', (id:string) => { declareAPI('getPersonaName', (id:string) => {
return getUserName() return getUserName()
}) })
luaEngine.global.set('getPersonaDescription', (id:string) => { declareAPI('getPersonaDescription', (id:string) => {
const db = getDatabase() const db = getDatabase()
const selectedChar = get(selectedCharID) const selectedChar = get(selectedCharID)
const char = db.characters[selectedChar] const char = db.characters[selectedChar]
@@ -490,12 +515,12 @@ export async function runLua(code:string, arg:{
return risuChatParser(getPersonaPrompt(), { chara: char }) return risuChatParser(getPersonaPrompt(), { chara: char })
}) })
luaEngine.global.set('getAuthorsNote', (id:string) => { declareAPI('getAuthorsNote', (id:string) => {
return luaEngineState.chat?.note ?? '' return ScriptingEngineState.chat?.note ?? ''
}) })
luaEngine.global.set('getBackgroundEmbedding', async (id:string) => { declareAPI('getBackgroundEmbedding', async (id:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
const db = getDatabase() const db = getDatabase()
@@ -504,8 +529,8 @@ export async function runLua(code:string, arg:{
return char.backgroundHTML return char.backgroundHTML
}) })
luaEngine.global.set('setBackgroundEmbedding', async (id:string, data:string) => { declareAPI('setBackgroundEmbedding', async (id:string, data:string) => {
if(!LuaSafeIds.has(id)){ if(!ScriptingSafeIds.has(id)){
return return
} }
const db = getDatabase() const db = getDatabase()
@@ -519,7 +544,7 @@ export async function runLua(code:string, arg:{
}) })
// Lore books // Lore books
luaEngine.global.set('getLoreBooksMain', (id:string, search: string) => { declareAPI('getLoreBooksMain', (id:string, search: string) => {
const db = getDatabase() const db = getDatabase()
const selectedChar = db.characters[get(selectedCharID)] const selectedChar = db.characters[get(selectedCharID)]
if (selectedChar.type !== 'character') { if (selectedChar.type !== 'character') {
@@ -532,8 +557,8 @@ export async function runLua(code:string, arg:{
return JSON.stringify(found.map((b) => ({ ...b, content: risuChatParser(b.content, { chara: selectedChar }) }))) return JSON.stringify(found.map((b) => ({ ...b, content: risuChatParser(b.content, { chara: selectedChar }) })))
}) })
luaEngine.global.set('loadLoreBooksMain', async (id:string, reserve:number) => { declareAPI('loadLoreBooksMain', async (id:string, reserve:number) => {
if(!LuaLowLevelIds.has(id)){ if(!ScriptingLowLevelIds.has(id)){
return return
} }
@@ -575,12 +600,12 @@ export async function runLua(code:string, arg:{
return JSON.stringify(loreBooks) return JSON.stringify(loreBooks)
}) })
luaEngine.global.set('axLLMMain', async (id:string, promptStr:string) => { declareAPI('axLLMMain', async (id:string, promptStr:string) => {
let prompt:{ let prompt:{
role: string, role: string,
content: string content: string
}[] = JSON.parse(promptStr) }[] = JSON.parse(promptStr)
if(!LuaLowLevelIds.has(id)){ if(!ScriptingLowLevelIds.has(id)){
return return
} }
let promptbody:OpenAIChat[] = prompt.map((dict) => { let promptbody:OpenAIChat[] = prompt.map((dict) => {
@@ -633,80 +658,83 @@ export async function runLua(code:string, arg:{
}) })
}) })
await luaEngine.doString(luaCodeWarper(code)) if(ScriptingEngineState.type === 'lua'){
luaEngineState.code = code await ScriptingEngineState.engine?.doString(luaCodeWarper(code))
}
ScriptingEngineState.code = code
} }
let accessKey = v4() let accessKey = v4()
if(mode === 'editDisplay'){ if(mode === 'editDisplay'){
LuaEditDisplayIds.add(accessKey) ScriptingEditDisplayIds.add(accessKey)
} }
else{ else{
LuaSafeIds.add(accessKey) ScriptingSafeIds.add(accessKey)
if(lowLevelAccess){ if(lowLevelAccess){
LuaLowLevelIds.add(accessKey) ScriptingLowLevelIds.add(accessKey)
} }
} }
let res:any let res:any
const luaEngine = luaEngineState.engine if(ScriptingEngineState.type === 'lua'){
try { const luaEngine = ScriptingEngineState.engine
switch(mode){ try {
case 'input':{ switch(mode){
const func = luaEngine.global.get('onInput') case 'input':{
if(func){ const func = luaEngine.global.get('onInput')
res = await func(accessKey) if(func){
res = await func(accessKey)
}
break
} }
break case 'output':{
} const func = luaEngine.global.get('onOutput')
case 'output':{ if(func){
const func = luaEngine.global.get('onOutput') res = await func(accessKey)
if(func){ }
res = await func(accessKey) break
} }
break case 'start':{
} const func = luaEngine.global.get('onStart')
case 'start':{ if(func){
const func = luaEngine.global.get('onStart') res = await func(accessKey)
if(func){ }
res = await func(accessKey) break
} }
break case 'onButtonClick':{
} const func = luaEngine.global.get('onButtonClick')
case 'onButtonClick':{ if(func){
const func = luaEngine.global.get('onButtonClick') res = await func(accessKey, data)
if(func){ }
res = await func(accessKey, data) break
} }
break case 'editRequest':
} case 'editDisplay':
case 'editRequest': case 'editInput':
case 'editDisplay': case 'editOutput':{
case 'editInput': const func = luaEngine.global.get('callListenMain')
case 'editOutput':{ if(func){
const func = luaEngine.global.get('callListenMain') res = await func(mode, accessKey, JSON.stringify(data))
if(func){ res = JSON.parse(res)
res = await func(mode, accessKey, JSON.stringify(data)) }
res = JSON.parse(res) break
} }
break default:{
} const func = luaEngine.global.get(mode)
default:{ if(func){
const func = luaEngine.global.get(mode) res = await func(accessKey)
if(func){ }
res = await func(accessKey) break
} }
break }
if(res === false){
stopSending = true
} }
} } catch (error) {
if(res === false){ console.error(error)
stopSending = true
} }
} catch (error) {
console.error(error)
} }
ScriptingSafeIds.delete(accessKey)
LuaSafeIds.delete(accessKey) ScriptingLowLevelIds.delete(accessKey)
LuaLowLevelIds.delete(accessKey) chat = ScriptingEngineState.chat
chat = luaEngineState.chat
return { return {
stopSending, chat, res stopSending, chat, res
@@ -756,8 +784,9 @@ async function ensureLuaFactory() {
async function getOrCreateEngineState( async function getOrCreateEngineState(
mode: string, mode: string,
): Promise<LuaEngineState> { type: 'lua'|'py'
let engineState = LuaEngines.get(mode); ): Promise<ScriptingEngineState> {
let engineState = ScriptingEngines.get(mode);
if (engineState) { if (engineState) {
return engineState; return engineState;
} }
@@ -768,10 +797,11 @@ async function getOrCreateEngineState(
} }
const creationPromise = (async () => { const creationPromise = (async () => {
const engineState: LuaEngineState = { const engineState: ScriptingEngineState = {
mutex: new Mutex(), mutex: new Mutex(),
type: type,
}; };
LuaEngines.set(mode, engineState); ScriptingEngines.set(mode, engineState);
pendingEngineCreations.delete(mode); pendingEngineCreations.delete(mode);
@@ -948,7 +978,7 @@ export async function runLuaEditTrigger<T extends any>(char:character|groupChat|
for(let trigger of triggers){ for(let trigger of triggers){
if(trigger?.effect?.[0]?.type === 'triggerlua'){ if(trigger?.effect?.[0]?.type === 'triggerlua'){
const runResult = await runLua(trigger.effect[0].code, { const runResult = await runScripted(trigger.effect[0].code, {
char: char, char: char,
lowLevelAccess: false, lowLevelAccess: false,
mode: mode, mode: mode,
@@ -975,7 +1005,7 @@ export async function runLuaButtonTrigger(char:character|groupChat|simpleCharact
for(let trigger of triggers){ for(let trigger of triggers){
if(trigger?.effect?.[0]?.type === 'triggerlua'){ if(trigger?.effect?.[0]?.type === 'triggerlua'){
runResult = await runLua(trigger.effect[0].code, { runResult = await runScripted(trigger.effect[0].code, {
char: char, char: char,
lowLevelAccess: trigger.lowLevelAccess, lowLevelAccess: trigger.lowLevelAccess,
mode: 'onButtonClick', mode: 'onButtonClick',

View File

@@ -8,7 +8,7 @@ import { selectSingleFile } from "../util";
import { assetRegex, type CbsConditions, risuChatParser as risuChatParserOrg, type simpleCharacterArgument } from "../parser.svelte"; import { assetRegex, type CbsConditions, risuChatParser as risuChatParserOrg, type simpleCharacterArgument } from "../parser.svelte";
import { getModuleAssets, getModuleRegexScripts, getModuleTriggers } from "./modules"; import { getModuleAssets, getModuleRegexScripts, getModuleTriggers } from "./modules";
import { HypaProcesser } from "./memory/hypamemory"; import { HypaProcesser } from "./memory/hypamemory";
import { runLuaEditTrigger } from "./lua"; import { runLuaEditTrigger } from "./scriptings";
import { pluginV2 } from "../plugins/plugins"; import { pluginV2 } from "../plugins/plugins";
import { runTrigger } from "./triggers"; import { runTrigger } from "./triggers";

View File

@@ -12,7 +12,7 @@ import { HypaProcesser } from "./memory/hypamemory";
import { requestChatData, type OpenAIChatExtra } from "./request"; import { requestChatData, type OpenAIChatExtra } from "./request";
import { generateAIImage } from "./stableDiff"; import { generateAIImage } from "./stableDiff";
import { writeInlayImage } from "./files/inlays"; import { writeInlayImage } from "./files/inlays";
import { runLua } from "./lua"; import { runScripted } from "./scriptings";
export interface triggerscript{ export interface triggerscript{
@@ -1148,7 +1148,7 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{
} }
case 'triggerlua':{ case 'triggerlua':{
const triggerCodeResult = await runLua(effect.code,{ const triggerCodeResult = await runScripted(effect.code,{
lowLevelAccess: trigger.lowLevelAccess, lowLevelAccess: trigger.lowLevelAccess,
mode: mode === 'manual' ? arg.manualName : mode, mode: mode === 'manual' ? arg.manualName : mode,
setVar: setVar, setVar: setVar,