From 92075e303f9488bde76c58150ff2b0a9f2fde4cb Mon Sep 17 00:00:00 2001 From: SMKRV Date: Mon, 18 Nov 2024 02:02:36 +0300 Subject: [PATCH] Update files --- custom_components/ha_text_ai/const.py | 12 ++++---- custom_components/ha_text_ai/coordinator.py | 30 ++++++++++++++++++-- custom_components/ha_text_ai/sensor.py | 30 ++++++++++++++------ ha_text_ai.zip | Bin 0 -> 6818 bytes 4 files changed, 56 insertions(+), 16 deletions(-) create mode 100644 ha_text_ai.zip diff --git a/custom_components/ha_text_ai/const.py b/custom_components/ha_text_ai/const.py index 27e9eb3..2f0c23c 100644 --- a/custom_components/ha_text_ai/const.py +++ b/custom_components/ha_text_ai/const.py @@ -18,13 +18,13 @@ DEFAULT_MAX_TOKENS = 1000 DEFAULT_API_ENDPOINT = "https://api.openai.com/v1" DEFAULT_REQUEST_INTERVAL = 1.0 +# Services +SERVICE_ASK_QUESTION = "ask_question" +SERVICE_CLEAR_HISTORY = "clear_history" +SERVICE_GET_HISTORY = "get_history" +SERVICE_SET_SYSTEM_PROMPT = "set_system_prompt" + # Attributes ATTR_QUESTION = "question" ATTR_RESPONSE = "response" ATTR_LAST_UPDATED = "last_updated" - -# Services -SERVICE_ASK_QUESTION = "ask_question -SERVICE_CLEAR_HISTORY = "clear_history" -SERVICE_GET_HISTORY = "get_history" -SERVICE_SET_SYSTEM_PROMPT = "set_system_prompt" diff --git a/custom_components/ha_text_ai/coordinator.py b/custom_components/ha_text_ai/coordinator.py index d1290b9..a79e9f1 100644 --- a/custom_components/ha_text_ai/coordinator.py +++ b/custom_components/ha_text_ai/coordinator.py @@ -12,6 +12,9 @@ from homeassistant.exceptions import ConfigEntryAuthFailed from .const import ( DOMAIN, DEFAULT_REQUEST_INTERVAL, + CONF_MODEL, + CONF_TEMPERATURE, + CONF_MAX_TOKENS, ) _LOGGER = logging.getLogger(__name__) @@ -44,6 +47,7 @@ class HATextAICoordinator(DataUpdateCoordinator): self.max_tokens = max_tokens self._question_queue = asyncio.Queue() self._responses: Dict[str, Any] = {} + self.system_prompt: Optional[str] = None openai.api_key = self.api_key if endpoint != "https://api.openai.com/v1": @@ -56,10 +60,15 @@ class HATextAICoordinator(DataUpdateCoordinator): try: question = await self._question_queue.get() - response = await self.hass.async_add_executor_job( + response_content = await self.hass.async_add_executor_job( self._make_api_call, question ) + response = { + "question": question, + "response": response_content + } self._responses[question] = response + _LOGGER.debug(f"Response from API: {response}") return self._responses except openai.error.AuthenticationError as err: @@ -71,9 +80,11 @@ class HATextAICoordinator(DataUpdateCoordinator): def _make_api_call(self, question: str) -> str: """Make API call to OpenAI.""" try: + messages = [{"role": "system", "content": self.system_prompt}] if self.system_prompt else [] + messages.append({"role": "user", "content": question}) completion = openai.chat.completions.create( model=self.model, - messages=[{"role": "user", "content": question}], + messages=messages, temperature=self.temperature, max_tokens=self.max_tokens, ) @@ -85,4 +96,19 @@ class HATextAICoordinator(DataUpdateCoordinator): async def async_ask_question(self, question: str) -> None: """Add question to queue.""" await self._question_queue.put(question) + _LOGGER.debug(f"Question added to queue: {question}") await self.async_refresh() + + def clear_history(self) -> None: + """Clear the stored question and response history.""" + self._responses.clear() + _LOGGER.info("History cleared.") + + def get_history(self, limit: int = 10) -> Dict[str, Any]: + """Get the history of questions and responses.""" + return {"history": list(self._responses.values())[-limit:]} + + def set_system_prompt(self, prompt: str) -> None: + """Set a system prompt that will be used for all future questions.""" + self.system_prompt = prompt + _LOGGER.info(f"System prompt set: {prompt}") diff --git a/custom_components/ha_text_ai/sensor.py b/custom_components/ha_text_ai/sensor.py index 28a4f97..43ec019 100644 --- a/custom_components/ha_text_ai/sensor.py +++ b/custom_components/ha_text_ai/sensor.py @@ -33,12 +33,26 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): self._config_entry = config_entry self._attr_unique_id = f"{config_entry.entry_id}" self._attr_name = "HA text AI" + self._attr_state_class = SensorStateClass.MEASUREMENT - @property - def extra_state_attributes(self) -> Optional[Dict[str, Any]]: - """Return entity specific state attributes.""" - return { - ATTR_QUESTION: list(self.coordinator.data.keys())[-1] if self.coordinator.data else None, - ATTR_RESPONSE: list(self.coordinator.data.values())[-1] if self.coordinator.data else None, - ATTR_LAST_UPDATED: self.coordinator.last_update_success_time, - } + @property + def state(self) -> StateType: + """Return the state of the sensor.""" + if self.coordinator.data: + return "Ready" # Assuming "Ready" is a valid state, you might want to return something meaningful, like the last response time. + return "Not Ready" + + @property + def extra_state_attributes(self) -> Optional[Dict[str, Any]]: + """Return entity specific state attributes.""" + if not self.coordinator.data: + return None + keys = list(self.coordinator.data.keys()) + values = list(self.coordinator.data.values()) + last_question = keys[-1] + last_response = values[-1] + return { + ATTR_QUESTION: last_question, + ATTR_RESPONSE: last_response, + ATTR_LAST_UPDATED: self.coordinator.last_update_success_time, + } diff --git a/ha_text_ai.zip b/ha_text_ai.zip new file mode 100644 index 0000000000000000000000000000000000000000..591e34ab6a245b6ae960b932cc0bfec4e64c24dd GIT binary patch literal 6818 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 N-8*YrNcQv7{{RXaw)Ox3 literal 0 HcmV?d00001