From 7a542c14d63d25fab27784b1297290fc22c9be7d Mon Sep 17 00:00:00 2001 From: Sunho Kim Date: Mon, 15 Jul 2024 07:26:50 -0700 Subject: [PATCH 1/3] Fix bug in lua low level access --- src/ts/process/lua.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ts/process/lua.ts b/src/ts/process/lua.ts index bd60865e..40e6550e 100644 --- a/src/ts/process/lua.ts +++ b/src/ts/process/lua.ts @@ -389,7 +389,7 @@ export async function runLua(code:string, arg:{ else{ LuaSafeIds.add(accessKey) if(lowLevelAccess){ - LuaLowLevelIds.add(v4()) + LuaLowLevelIds.add(accessKey) } } let res:any From cb9514c50813527aad70043f8d64602eb60674e5 Mon Sep 17 00:00:00 2001 From: Sunho Kim Date: Mon, 15 Jul 2024 21:11:21 -0700 Subject: [PATCH 2/3] fix: prevent lua engine getting killed and pool engines --- src/ts/mutex.ts | 88 +++++ src/ts/process/lua.ts | 789 ++++++++++++++++++++++-------------------- 2 files changed, 493 insertions(+), 384 deletions(-) create mode 100644 src/ts/mutex.ts diff --git a/src/ts/mutex.ts b/src/ts/mutex.ts new file mode 100644 index 00000000..8b5e156c --- /dev/null +++ b/src/ts/mutex.ts @@ -0,0 +1,88 @@ +/** + * A lock for synchronizing async operations. + * Use this to protect a critical section + * from getting modified by multiple async operations + * at the same time. + */ +export class Mutex { + /** + * When multiple operations attempt to acquire the lock, + * this queue remembers the order of operations. + */ + private _queue: { + resolve: (release: ReleaseFunction) => void + }[] = [] + + private _isLocked = false + + /** + * Wait until the lock is acquired. + * @returns A function that releases the acquired lock. + */ + acquire() { + return new Promise((resolve) => { + this._queue.push({resolve}) + this._dispatch() + }); + } + + /** + * Enqueue a function to be run serially. + * + * This ensures no other functions will start running + * until `callback` finishes running. + * @param callback Function to be run exclusively. + * @returns The return value of `callback`. + */ + async runExclusive(callback: () => Promise) { + const release = await this.acquire() + try { + return await callback() + } finally { + release() + } + } + + /** + * Check the availability of the resource + * and provide access to the next operation in the queue. + * + * _dispatch is called whenever availability changes, + * such as after lock acquire request or lock release. + */ + private _dispatch() { + if (this._isLocked) { + // The resource is still locked. + // Wait until next time. + return + } + const nextEntry = this._queue.shift() + if (!nextEntry) { + // There is nothing in the queue. + // Do nothing until next dispatch. + return + } + // The resource is available. + this._isLocked = true + // and give access to the next operation + // in the queue. + nextEntry.resolve(this._buildRelease()) + } + + /** + * Build a release function for each operation + * so that it can release the lock after + * the operation is complete. + */ + private _buildRelease(): ReleaseFunction { + return () => { + // Each release function make + // the resource available again + this._isLocked = false + // and call dispatch. + this._dispatch() + } + } +} + +type ReleaseFunction = () => void \ No newline at end of file diff --git a/src/ts/process/lua.ts b/src/ts/process/lua.ts index 40e6550e..e4477cc1 100644 --- a/src/ts/process/lua.ts +++ b/src/ts/process/lua.ts @@ -11,14 +11,21 @@ import type { OpenAIChat } from "."; import { requestChatData } from "./request"; import { v4 } from "uuid"; import { getModuleTriggers } from "./modules"; +import { Mutex } from "../mutex"; let luaFactory:LuaFactory -let luaEngine:LuaEngine -let lastCode = '' let LuaSafeIds = new Set() let LuaEditDisplayIds = new Set() let LuaLowLevelIds = new Set() +interface LuaEngineState { + code: string; + engine: LuaEngine; + mutex: Mutex; +} + +let LuaEngines = new Map() + export async function runLua(code:string, arg:{ char?:character|groupChat|simpleCharacterArgument, chat?:Chat @@ -37,412 +44,426 @@ export async function runLua(code:string, arg:{ let stopSending = false let lowLevelAccess = arg.lowLevelAccess ?? false - if(!luaEngine || lastCode !== code){ - if(luaEngine){ - luaEngine.global.close() + if(!luaFactory){ + await makeLuaFactory() + } + let luaEngineState = LuaEngines.get(mode) + let wasEmpty = false + if (!luaEngineState) { + luaEngineState = { + code, + engine: await luaFactory.createEngine({injectObjects: true}), + mutex: new Mutex() } - if(!luaFactory){ - makeLuaFactory() - } - luaEngine = await luaFactory.createEngine({injectObjects: true}) - luaEngine.global.set('setChatVar', (id:string,key:string, value:string) => { - if(!LuaSafeIds.has(id) && !LuaEditDisplayIds.has(id)){ - return - } - setVar(key, value) - }) - luaEngine.global.set('getChatVar', (id:string,key:string) => { - if(!LuaSafeIds.has(id) && !LuaEditDisplayIds.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('removeChat', (id:string, index:number) => { - if(!LuaSafeIds.has(id)){ - return - } - chat.message.splice(index, 1) - 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('getFullChatMain', (id:string) => { - const data = JSON.stringify(chat.message.map((v) => { - return { - role: v.role, - data: v.data - } - })) - return data - }) - - luaEngine.global.set('setFullChatMain', (id:string, value:string) => { - const realValue = JSON.parse(value) - if(!LuaSafeIds.has(id)){ - return - } - chat.message = realValue.map((v) => { - return { - role: v.role, - data: v.data + LuaEngines.set(mode, luaEngineState) + wasEmpty = true + } + return await luaEngineState.mutex.runExclusive(async () => { + if (wasEmpty || code !== luaEngineState.code) { + if (!wasEmpty) + luaEngineState.engine.global.close() + luaEngineState.engine = await luaFactory.createEngine({injectObjects: true}) + const luaEngine = luaEngineState.engine + luaEngine.global.set('setChatVar', (id:string,key:string, value:string) => { + if(!LuaSafeIds.has(id) && !LuaEditDisplayIds.has(id)){ + return } + setVar(key, value) + }) + luaEngine.global.set('getChatVar', (id:string,key:string) => { + if(!LuaSafeIds.has(id) && !LuaEditDisplayIds.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('removeChat', (id:string, index:number) => { + if(!LuaSafeIds.has(id)){ + return + } + chat.message.splice(index, 1) + 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('getFullChatMain', (id:string) => { + const data = JSON.stringify(chat.message.map((v) => { + return { + role: v.role, + data: v.data + } + })) + return data }) - CurrentChat.set(chat) - }) - luaEngine.global.set('logMain', (value:string) => { - console.log(JSON.parse(value)) - }) + luaEngine.global.set('setFullChatMain', (id:string, value:string) => { + const realValue = JSON.parse(value) + if(!LuaSafeIds.has(id)){ + return + } + chat.message = realValue.map((v) => { + return { + role: v.role, + data: v.data + } + }) + CurrentChat.set(chat) + }) - //Low Level Access - luaEngine.global.set('similarity', async (id:string, source:string, value:string[]) => { - if(!LuaLowLevelIds.has(id)){ - return - } - const processer = new HypaProcesser('MiniLM') - await processer.addText(value) - return await processer.similaritySearch(source) - }) + luaEngine.global.set('logMain', (value:string) => { + console.log(JSON.parse(value)) + }) - luaEngine.global.set('generateImage', async (id:string, value:string, negValue:string = '') => { - if(!LuaLowLevelIds.has(id)){ - return - } - const gen = await generateAIImage(value, char as character, negValue, 'inlay') - if(!gen){ - return 'Error: Image generation failed' - } - const imgHTML = new Image() - imgHTML.src = gen - const inlay = await writeInlayImage(imgHTML) - return `{{inlay::${inlay}}}` - }) + //Low Level Access + luaEngine.global.set('similarity', async (id:string, source:string, value:string[]) => { + if(!LuaLowLevelIds.has(id)){ + return + } + const processer = new HypaProcesser('MiniLM') + await processer.addText(value) + return await processer.similaritySearch(source) + }) - luaEngine.global.set('LLMMain', async (id:string, promptStr:string) => { - let prompt:{ - role: string, - content: string - }[] = JSON.parse(promptStr) - 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 + luaEngine.global.set('generateImage', async (id:string, value:string, negValue:string = '') => { + if(!LuaLowLevelIds.has(id)){ + return + } + const gen = await generateAIImage(value, char as character, 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('LLMMain', async (id:string, promptStr:string) => { + let prompt:{ + role: string, + content: string + }[] = JSON.parse(promptStr) + 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 JSON.stringify({ + success: false, + result: 'Error: ' + result.result + }) + } + + if(result.type === 'streaming' || result.type === 'multiline'){ + return JSON.stringify({ + success: false, + result: result.result + }) + } + + return JSON.stringify({ + success: true, + result: result.result + }) + }) + + luaEngine.global.set('simpleLLM', async (id:string, prompt:string) => { + if(!LuaLowLevelIds.has(id)){ + return + } + 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 { - content: dict['content'] ?? '', - role: role, - } - }) - const result = await requestChatData({ - formated: promptbody, - bias: {}, - useStreaming: false, - noMultiGen: true, - }, 'model') - - if(result.type === 'fail'){ - return JSON.stringify({ - success: false, - result: 'Error: ' + result.result - }) - } - - if(result.type === 'streaming' || result.type === 'multiline'){ - return JSON.stringify({ - success: false, - result: result.result - }) - } - - return JSON.stringify({ - success: true, - result: result.result - }) - }) - - luaEngine.global.set('simpleLLM', async (id:string, prompt:string) => { - if(!LuaLowLevelIds.has(id)){ - return - } - 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, + success: true, result: result.result } - } + }) + + luaEngine.global.set('getName', async (id:string) => { + if(!LuaSafeIds.has(id)){ + return + } + const db = get(DataBase) + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + return char.name + }) - return { - success: true, - result: result.result - } - }) - - luaEngine.global.set('getName', async (id:string) => { - if(!LuaSafeIds.has(id)){ - return - } - const db = get(DataBase) - const selectedChar = get(selectedCharID) - const char = db.characters[selectedChar] - return char.name - }) + luaEngine.global.set('setName', async (id:string, name:string) => { + if(!LuaSafeIds.has(id)){ + return + } + const db = get(DataBase) + const selectedChar = get(selectedCharID) + if(typeof name !== 'string'){ + throw('Invalid data type') + } + db.characters[selectedChar].name = name + setDatabase(db) + }) - luaEngine.global.set('setName', async (id:string, name:string) => { - if(!LuaSafeIds.has(id)){ - return - } - const db = get(DataBase) - const selectedChar = get(selectedCharID) - if(typeof name !== 'string'){ - throw('Invalid data type') - } - db.characters[selectedChar].name = name - setDatabase(db) - }) + luaEngine.global.set('setDescription', async (id:string, desc:string) => { + if(!LuaSafeIds.has(id)){ + return + } + const db = get(DataBase) + const selectedChar = get(selectedCharID) + const char =db.characters[selectedChar] + if(typeof data !== 'string'){ + throw('Invalid data type') + } + if(char.type === 'group'){ + throw('Character is a group') + } + char.desc = desc + db.characters[selectedChar] = char + setDatabase(db) + }) - luaEngine.global.set('setDescription', async (id:string, desc:string) => { - if(!LuaSafeIds.has(id)){ - return - } - const db = get(DataBase) - const selectedChar = get(selectedCharID) - const char =db.characters[selectedChar] - if(typeof data !== 'string'){ - throw('Invalid data type') - } - if(char.type === 'group'){ - throw('Character is a group') - } - char.desc = desc - db.characters[selectedChar] = char - setDatabase(db) - }) + luaEngine.global.set('setCharacterFirstMessage', async (id:string, data:string) => { + if(!LuaSafeIds.has(id)){ + return + } + const db = get(DataBase) + 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 + }) - luaEngine.global.set('setCharacterFirstMessage', async (id:string, data:string) => { - if(!LuaSafeIds.has(id)){ - return - } - const db = get(DataBase) - 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 - }) + luaEngine.global.set('getCharacterFirstMessage', async (id:string) => { + if(!LuaSafeIds.has(id)){ + return + } + const db = get(DataBase) + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + return char.firstMessage + }) - luaEngine.global.set('getCharacterFirstMessage', async (id:string) => { - if(!LuaSafeIds.has(id)){ - return - } - const db = get(DataBase) - const selectedChar = get(selectedCharID) - const char = db.characters[selectedChar] - return char.firstMessage - }) + luaEngine.global.set('getBackgroundEmbedding', async (id:string) => { + if(!LuaSafeIds.has(id)){ + return + } + const db = get(DataBase) + const selectedChar = get(selectedCharID) + const char = db.characters[selectedChar] + return char.backgroundHTML + }) - luaEngine.global.set('getBackgroundEmbedding', async (id:string) => { - if(!LuaSafeIds.has(id)){ - return - } - const db = get(DataBase) - const selectedChar = get(selectedCharID) - const char = db.characters[selectedChar] - return char.backgroundHTML - }) + luaEngine.global.set('setBackgroundEmbedding', async (id:string, data:string) => { + if(!LuaSafeIds.has(id)){ + return + } + const db = get(DataBase) + const selectedChar = get(selectedCharID) + if(typeof data !== 'string'){ + return false + } + db.characters[selectedChar].backgroundHTML = data + setDatabase(db) + return true + }) - luaEngine.global.set('setBackgroundEmbedding', async (id:string, data:string) => { - if(!LuaSafeIds.has(id)){ - return - } - const db = get(DataBase) - const selectedChar = get(selectedCharID) - if(typeof data !== 'string'){ - return false - } - db.characters[selectedChar].backgroundHTML = data - setDatabase(db) - return true - }) - - await luaEngine.doString(luaCodeWarper(code)) - lastCode = code - } - let accessKey = v4() - if(mode === 'editDisplay'){ - LuaEditDisplayIds.add(accessKey) - } - else{ - LuaSafeIds.add(accessKey) - if(lowLevelAccess){ - LuaLowLevelIds.add(accessKey) + await luaEngine.doString(luaCodeWarper(code)) + luaEngineState.code = code } - } - let res:any - try { - 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 'editRequest': - case 'editDisplay': - case 'editInput': - case 'editOutput':{ - const func = luaEngine.global.get('callListenMain') - if(func){ - res = await func(mode, accessKey, JSON.stringify(data)) - res = JSON.parse(res) - } - } - default:{ - const func = luaEngine.global.get(mode) - if(func){ - res = await func(accessKey) - } - } - } - if(res === false){ - stopSending = true + let accessKey = v4() + if(mode === 'editDisplay'){ + LuaEditDisplayIds.add(accessKey) + } + else{ + LuaSafeIds.add(accessKey) + if(lowLevelAccess){ + LuaLowLevelIds.add(accessKey) + } + } + let res:any + const luaEngine = luaEngineState.engine + try { + 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 'editRequest': + case 'editDisplay': + case 'editInput': + case 'editOutput':{ + const func = luaEngine.global.get('callListenMain') + if(func){ + res = await func(mode, accessKey, JSON.stringify(data)) + res = JSON.parse(res) + } + } + default:{ + const func = luaEngine.global.get(mode) + if(func){ + res = await func(accessKey) + } + } + } + if(res === false){ + stopSending = true + } + } catch (error) { + console.error(error) } - } catch (error) { - console.error(error) - } - LuaSafeIds.delete(accessKey) - LuaLowLevelIds.delete(accessKey) + LuaSafeIds.delete(accessKey) + LuaLowLevelIds.delete(accessKey) - return { - stopSending, chat, res - } + return { + stopSending, chat, res + } + }) } async function makeLuaFactory(){ From 5b4a4f0e304327dd14730ad0e5e2d35c85ad4076 Mon Sep 17 00:00:00 2001 From: Sunho Kim Date: Mon, 15 Jul 2024 21:12:14 -0700 Subject: [PATCH 3/3] fix: add missing breaks --- src/ts/process/lua.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ts/process/lua.ts b/src/ts/process/lua.ts index e4477cc1..c327ab9e 100644 --- a/src/ts/process/lua.ts +++ b/src/ts/process/lua.ts @@ -420,18 +420,21 @@ export async function runLua(code:string, arg:{ if(func){ res = await func(accessKey) } + break } case 'output':{ const func = luaEngine.global.get('onOutput') if(func){ res = await func(accessKey) } + break } case 'start':{ const func = luaEngine.global.get('onStart') if(func){ res = await func(accessKey) } + break } case 'editRequest': case 'editDisplay': @@ -442,12 +445,14 @@ export async function runLua(code:string, arg:{ res = await func(mode, accessKey, JSON.stringify(data)) res = JSON.parse(res) } + break } default:{ const func = luaEngine.global.get(mode) if(func){ res = await func(accessKey) } + break } } if(res === false){