UNPKG

iobroker.ai-assistant

Version:

AI Assistant adapter allows you to control your ioBroker trought artifical intelligence based on LLMs

278 lines (259 loc) 11.2 kB
"use strict"; const schedule = require("node-schedule"); /** * The SchedulerTool class * This class is used to handle the scheduler tool */ class SchedulerTool { /** * Constructor for the SchedulerTool * * @param adapter - The adapter instance */ constructor(adapter) { this.adapter = adapter; this.adapter.log.debug(`Created new SchedulerTool`); this.name = "SchedulerTool"; this.temperature = 0.2; this.max_tokens = 4000; this.response_format = `{ "reasoning": "${this.adapter.I18n.translate("scheduler_chain_of_thought")}", "noticeToAssistant": "${this.adapter.I18n.translate("scheduler_notice_to_assistant")}", "createTimeouts": [{"timeoutSeconds": "${this.adapter.I18n.translate("scheduler_timeout_seconds_till_execution")}", "instruction": "${this.adapter.I18n.translate("scheduler_timeout_instruction")}"}], "createCronjobs": [{"cronExpression": "${this.adapter.I18n.translate("scheduler_cronjob_definition")}", "instruction": "${this.adapter.I18n.translate("scheduler_cronjob_instruction")}"}], "deleteCronjobs": ["${this.adapter.I18n.translate("scheduler_delete_id")}"] }`; this.system_prompt = ` ${this.adapter.I18n.translate("scheduler_prompt_1")} ${this.adapter.I18n.translate("scheduler_prompt_2")} ${this.adapter.I18n.translate("scheduler_prompt_3")} ${this.response_format} `; this.tool_message = ` ${this.adapter.I18n.translate("scheduler_current_time")} ###TIME### ${this.adapter.I18n.translate("scheduler_created")} ###CRONJOBS### ${this.adapter.I18n.translate("scheduler_instruction")} "###PROMPT###". ${this.adapter.I18n.translate("scheduler_answer")} `; this.prompt = null; this.cronJobs = []; this.initCronJobs(); } /** * Initializes the cron jobs for the scheduler tool * * @example * await schedulerTool.initCronJobs(); */ async initCronJobs() { this.adapter.log.debug(`${this.name}: Initializing cron jobs for ${this.name}`); for (const job in this.cronJobs) { schedule.cancelJob(job); } this.cronJobs = []; const cronjobs = await this.getCronjobs(); for (const job of cronjobs) { try { this.createCronJob(job.id, job.cron, job.instruction); } catch (e) { this.adapter.log.error(`${this.name}: Error while parsing cron job data for ${job.id}: ${e}`); } } } /** * Gets all cron jobs for the scheduler tool * * @returns - Array of cron jobs */ async getCronjobs() { this.adapter.log.debug(`${this.name}: Getting cron jobs for ${this.name}`); const allObjects = await this.adapter.getAdapterObjectsAsync(); const cronjobs = []; for (const id in allObjects) { if (id.includes("Cronjobs.")) { const state = await this.adapter.getStateAsync(id); try { const cronData = JSON.parse(this.adapter.jsonRepair(state.val)); if (cronData && cronData.cron && cronData.instruction) { this.adapter.log.debug( `${this.name}: Found cron job for ${id} with cron ${cronData.cron} and instruction ${cronData.instruction}`, ); cronjobs.push({ id: id, cron: cronData.cron, instruction: cronData.instruction }); } } catch (e) { this.adapter.log.error(`${this.name}: Error while parsing cron job data for ${id}: ${e}`); } } } return cronjobs; } /** * Creates a cron job for the scheduler tool * * @param objectId - The object id * @param cronExpression - The cron expression * @param instruction - The instruction * @returns - Nothing */ createCronJob(objectId, cronExpression, instruction) { if (!objectId || !cronExpression || !instruction) { return this.adapter.log.warn( `Cannot create cron job for ${objectId} with expression ${cronExpression} and instruction ${instruction}`, ); } this.adapter.log.debug( `${this.name}: Creating cronjob for ${objectId} with expression ${cronExpression} and instruction ${instruction}`, ); const job = schedule.scheduleJob(cronExpression, async () => { this.adapter.log.info(`${this.name}: Executing cron job ${objectId} with instruction ${instruction}`); const cronExecutionData = { type: "wakeUpFromCronjob", tool: this.name, prompt: instruction, noticeToAssistant: this.adapter.I18n.translate("scheduler_execute", cronExpression), result: this.adapter.I18n.translate("scheduler_current_time") + new Date().toLocaleString(), }; this.adapter.startAssistantRequest(JSON.stringify(cronExecutionData)); }); this.cronJobs.push({ objectId, job }); } /** * Deletes a cron job for the scheduler tool * * @param objectId - The object id * @returns - Nothing */ async deleteCronjob(objectId) { this.adapter.log.debug(`${this.name}: Deleting cron job for ${objectId}`); try { await this.adapter.delObjectAsync(objectId); this.initCronJobs(); } catch (e) { this.adapter.log.error(`${this.name}: Error while deleting cron job for ${objectId}: ${e}`); } } /** * Start a request for the scheduler tool * * @param prompt - The prompt for the request * @returns - The response of the tool */ async request(prompt) { this.adapter.log.debug(`${this.name}: Request data for ${this.name} with prompt: ${prompt}`); const modelResponse = await this.adapter.startModelRequest( [{ role: "user", content: await this.constructToolMessage(prompt) }], this.system_prompt.replace(/(\r\n|\n|\t|\r)/gm, ""), this.max_tokens, this.temperature, ); const toolResponse = await this.handleResponse(modelResponse.text); return toolResponse; } /** * Constructs the tool message for the scheduler tool * * @param prompt - The prompt for the tool message * @returns - The tool message */ async constructToolMessage(prompt) { let message = this.tool_message; const cronjobs = await this.getCronjobs(); message = message.replace("###CRONJOBS###", JSON.stringify(cronjobs)); message = message.replace("###PROMPT###", prompt); message = message.replace("###TIME###", new Date().toLocaleString()); return message; } /** * Handles the response for the scheduler tool * * @param modelResponse - The model response * @returns - The response of the tool */ async handleResponse(modelResponse) { this.adapter.log.debug(`${this.name}: Handling response for ${this.name}`); try { const responseData = JSON.parse(this.adapter.jsonRepair(modelResponse.replace(/(\r\n|\n|\t|\r)/gm, ""))); const createdCronjobs = []; const deletedCronjobs = []; const createdTimeouts = []; if ( responseData.createCronjobs && Array.isArray(responseData.createCronjobs) && responseData.createCronjobs.length > 0 ) { for (const cronjob of responseData.createCronjobs) { const id = `Cronjobs.${Date.now()}`; await this.adapter.setObjectAsync(id, { type: "state", common: { name: `Cron job for ${this.name}`, type: "string", role: "state", read: true, write: true, }, }); const cronjobString = JSON.stringify({ cron: cronjob.cronExpression, instruction: cronjob.instruction, }); await this.adapter.setStateAsync(id, cronjobString); this.createCronJob(id, cronjob.cronExpression, cronjob.instruction); createdCronjobs.push({ cronExpression: cronjob.cronExpression, instruction: cronjob.instruction }); } } if ( responseData.createTimeouts && Array.isArray(responseData.createTimeouts) && responseData.createTimeouts.length > 0 ) { for (const timeout of responseData.createTimeouts) { this.adapter.setTimeout( () => { const timeoutExecutionData = { type: "wakeUpFromTimeout", tool: "SchedulerTool", prompt: timeout.instruction, noticeToAssistant: this.adapter.I18n.translate( "scheduler_execute_timeout", timeout.timeoutSeconds, ), result: this.adapter.I18n.translate("scheduler_current_time") + new Date().toLocaleString(), }; this.adapter.startAssistantRequest(JSON.stringify(timeoutExecutionData)); }, timeout.timeoutSeconds * 1000, timeout, ); createdTimeouts.push({ timeoutSeconds: timeout.timeoutSeconds, instruction: timeout.instruction }); } } if ( responseData.deleteCronjobs && Array.isArray(responseData.deleteCronjobs) && responseData.deleteCronjobs.length > 0 ) { for (const id of responseData.deleteCronjobs) { await this.deleteCronjob(id); deletedCronjobs.push(id); } } const toolResponse = { type: "toolResponse", tool: this.name, reasoning: responseData.reasoning, noticeToAssistant: responseData.noticeToAssistant, result: { createdCronjobs: createdCronjobs, deletedCronjobs: deletedCronjobs, createdTimeouts: createdTimeouts, }, }; return toolResponse; } catch (error) { this.adapter.log.error(`${this.name}: Error while parsing response for ${this.name}: ${error}`); return null; } } } module.exports = SchedulerTool;