diff --git a/server/lib/gateway/gateway.forwardMessageToOpenAI.js b/server/lib/gateway/gateway.forwardMessageToOpenAI.js index 217dcdf865..949ed143f8 100644 --- a/server/lib/gateway/gateway.forwardMessageToOpenAI.js +++ b/server/lib/gateway/gateway.forwardMessageToOpenAI.js @@ -1,3 +1,4 @@ +const logger = require('../../utils/logger'); const { Error429 } = require('../../utils/httpErrors'); const intentTranslation = { @@ -26,10 +27,28 @@ const disableOpenAiFirstReply = new Set(['GET_TEMPERATURE', 'GET_HUMIDITY']); */ async function forwardMessageToOpenAI({ message, image, previousQuestions, context }) { try { + const devices = this.stateManager.getAllKeys('device').map((deviceSelector) => { + const d = this.stateManager.get('device', deviceSelector); + return { + name: d.name, + room: d.room ? d.room.name : '', + features: d.features.map((f) => ({ + name: f.name, + selector: f.selector, + category: f.category, + type: f.type, + last_value: f.last_value, + last_value_changed: f.last_value_changed, + unit: f.unit, + })), + }; + }); + const response = await this.openAIAsk({ question: message.text, image, previous_questions: previousQuestions, + devices, }); const classification = {}; @@ -65,6 +84,7 @@ async function forwardMessageToOpenAI({ message, image, previousQuestions, conte return classification; } catch (e) { + logger.warn(e); if (e instanceof Error429) { await this.message.replyByIntent(message, 'openai.request.tooManyRequests', context); } else { diff --git a/server/lib/state/index.js b/server/lib/state/index.js index 739717f70b..c0192773e2 100644 --- a/server/lib/state/index.js +++ b/server/lib/state/index.js @@ -32,6 +32,17 @@ function get(entity, entitySelector) { return this.state[entity][entitySelector].get(); } +/** + * @description Return all keys. + * @param {string} entity - The type of entity we should get the value from. + * @returns {any} Return the full state in store. + * @example + * stateManager.get('device', 'main-lamp'); + */ +function getAllKeys(entity) { + return Object.keys(this.state[entity]); +} + /** * @description Return the value of a key in the store. * @param {string} entity - The type of entity we should get the value from. @@ -85,5 +96,6 @@ StateManager.prototype.setState = setState; StateManager.prototype.deleteState = deleteState; StateManager.prototype.get = get; StateManager.prototype.getKey = getKey; +StateManager.prototype.getAllKeys = getAllKeys; module.exports = StateManager; diff --git a/server/test/lib/gateway/gateway.forwardMessageToOpenAI.test.js b/server/test/lib/gateway/gateway.forwardMessageToOpenAI.test.js index 7ce2e75429..62c372e8f4 100644 --- a/server/test/lib/gateway/gateway.forwardMessageToOpenAI.test.js +++ b/server/test/lib/gateway/gateway.forwardMessageToOpenAI.test.js @@ -3,6 +3,7 @@ const { expect } = require('chai'); const proxyquire = require('proxyquire').noCallThru(); const EventEmitter = require('events'); +const StateManager = require('../../../lib/state'); const GladysGatewayClientMock = require('./GladysGatewayClientMock.test'); const event = new EventEmitter(); @@ -48,7 +49,88 @@ describe('gateway.forwardMessageToOpenAI', () => { getEntityIdByName: fake.returns('14a8ad23-78fa-45e4-8583-f5452792818d'), }; const serviceManager = {}; - const stateManager = {}; + const stateManager = new StateManager(event); + stateManager.setState('device', 'co2-sensor', { + id: 'bd2eaaef-5baa-4aa6-8c94-ccce01026a53', + service_id: '59d7e9e2-a5a5-43b0-8796-287e00670355', + room_id: '3dc8243a-983f-45bc-94e8-f0c651b78c5d', + name: 'Capteur salon', + selector: 'mqtt-test', + model: null, + external_id: 'mqtt:test', + should_poll: false, + poll_frequency: null, + created_at: '2024-08-10T07:34:51.798Z', + updated_at: '2024-10-18T02:00:49.733Z', + features: [ + { + id: '31fc42aa-f338-40c1-bcc0-c14d37470e71', + device_id: 'bd2eaaef-5baa-4aa6-8c94-ccce01026a53', + name: 'Batterie', + selector: 'mqtt-battery', + external_id: 'mqtt:battery', + category: 'battery', + type: 'integer', + read_only: true, + keep_history: true, + has_feedback: false, + unit: 'percent', + min: 0, + max: 100, + last_value: null, + last_value_string: null, + last_value_changed: null, + last_hourly_aggregate: null, + last_daily_aggregate: null, + last_monthly_aggregate: null, + created_at: '2024-10-04T01:10:45.283Z', + updated_at: '2024-10-04T01:11:22.787Z', + }, + { + id: '444b306d-5a2c-49f6-a8a5-ffe2a1d7cb11', + device_id: 'bd2eaaef-5baa-4aa6-8c94-ccce01026a53', + name: 'Niveau de Co2', + selector: 'mqtt-co2', + external_id: 'mqtt:co2', + category: 'co2-sensor', + type: 'integer', + read_only: true, + keep_history: true, + has_feedback: false, + unit: 'ppm', + min: 0, + max: 100000, + last_value: 1200, + last_value_string: null, + last_value_changed: '2024-10-18T01:40:12.042Z', + last_hourly_aggregate: null, + last_daily_aggregate: null, + last_monthly_aggregate: null, + created_at: '2024-10-04T01:11:22.783Z', + updated_at: '2024-10-18T02:01:16.000Z', + }, + ], + params: [], + room: { + id: '3dc8243a-983f-45bc-94e8-f0c651b78c5d', + house_id: 'ec0e36a8-f370-4157-9249-3892a6e3a52c', + name: 'salon', + selector: 'salon', + created_at: '2024-10-11T06:43:37.620Z', + updated_at: '2024-10-11T06:43:37.620Z', + }, + service: { + id: '59d7e9e2-a5a5-43b0-8796-287e00670355', + pod_id: null, + name: 'mqtt', + selector: 'mqtt', + version: '0.1.0', + has_message_feature: false, + status: 'RUNNING', + created_at: '2024-08-08T12:59:46.450Z', + updated_at: '2024-10-18T02:08:20.564Z', + }, + }); gateway = new Gateway( variable, event, @@ -78,6 +160,37 @@ describe('gateway.forwardMessageToOpenAI', () => { room: 'living room', }); const classification = await gateway.forwardMessageToOpenAI({ message, previousQuestions, context }); + assert.calledWith(gateway.gladysGatewayClient.openAIAsk, { + question: 'Turn on the light in the living room', + devices: [ + { + name: 'Capteur salon', + room: 'salon', + features: [ + { + name: 'Batterie', + selector: 'mqtt-battery', + category: 'battery', + type: 'integer', + last_value: null, + last_value_changed: null, + unit: 'percent', + }, + { + name: 'Niveau de Co2', + selector: 'mqtt-co2', + category: 'co2-sensor', + type: 'integer', + last_value: 1200, + last_value_changed: '2024-10-18T01:40:12.042Z', + unit: 'ppm', + }, + ], + }, + ], + image: undefined, + previous_questions: [], + }); expect(classification).to.deep.equal({ entities: [ { diff --git a/server/test/lib/state/state.test.js b/server/test/lib/state/state.test.js index caaae58706..b0d685266d 100644 --- a/server/test/lib/state/state.test.js +++ b/server/test/lib/state/state.test.js @@ -34,6 +34,14 @@ describe('state', () => { const userSleepState = stateManager.getKey('user', 'tony', 'sleep'); expect(userSleepState).to.equal('asleep'); }); + it('should get all users keys', async () => { + const stateManager = new StateManager(event); + stateManager.setState('user', 'tony', { + sleep: 'asleep', + }); + const keys = stateManager.getAllKeys('user'); + expect(keys).to.deep.equal(['tony']); + }); it('should return null', async () => { const stateManager = new StateManager(event); const userSleepState = stateManager.getKey('user', 'tony', 'sleep');