From 4f46d077df1110eca2aa0c775b02f8b21d49fb1a Mon Sep 17 00:00:00 2001 From: SMKRV Date: Sun, 24 Nov 2024 01:54:09 +0300 Subject: [PATCH] Release v2.0.0 --- custom_components/ha_text_ai/coordinator.py | 75 ++++++++++++++++++++- custom_components/ha_text_ai/sensor.py | 45 +++++++------ 2 files changed, 98 insertions(+), 22 deletions(-) diff --git a/custom_components/ha_text_ai/coordinator.py b/custom_components/ha_text_ai/coordinator.py index 8096a47..5787499 100644 --- a/custom_components/ha_text_ai/coordinator.py +++ b/custom_components/ha_text_ai/coordinator.py @@ -99,6 +99,7 @@ class HATextAICoordinator(DataUpdateCoordinator): self.client = None self.hass = hass # Store hass reference for _init_client self._start_time = time.time() + self.last_response = None # History and metrics self._history: List[Dict[str, Any]] = [] @@ -507,7 +508,7 @@ class HATextAICoordinator(DataUpdateCoordinator): async def __aexit__(self, exc_type, exc_val, exc_tb): """Async exit.""" await self.async_stop() - + async def async_start(self) -> None: """Start coordinator operations.""" if not self._is_ready: @@ -563,13 +564,83 @@ class HATextAICoordinator(DataUpdateCoordinator): return { "status": self.endpoint_status, "metrics": self._performance_metrics, - "last_update": dt_util.utcnow().isoformat() + "last_update": dt_util.utcnow().isoformat(), + "last_response": self.last_response, # Добавляем last_response + "is_processing": self._is_processing, + "is_rate_limited": self._is_rate_limited, + "is_maintenance": self._is_maintenance, } except Exception as e: self._last_error = str(e) _LOGGER.error("Error updating data: %s", str(e)) raise HomeAssistantError(f"Error updating data: {str(e)}") + async def async_process_message(self, message: str, **kwargs) -> dict: + """Process message and update last_response.""" + try: + self._is_processing = True + self.async_update_listeners() + + start_time = time.time() + + if self._is_anthropic: + response = await self.client.messages.create( + model=self.model, + max_tokens=self.max_tokens, + temperature=self.temperature, + messages=[{"role": "user", "content": message}], + **kwargs + ) + response_text = response.content[0].text + else: + response = await self.client.chat.completions.create( + model=self.model, + temperature=self.temperature, + max_tokens=self.max_tokens, + messages=[{"role": "user", "content": message}], + **kwargs + ) + response_text = response.choices[0].message.content + + elapsed_time = time.time() - start_time + self._request_count += 1 + self._performance_metrics["avg_response_time"] = ( + (self._performance_metrics["avg_response_time"] * (self._request_count - 1) + elapsed_time) + / self._request_count + ) + + self.last_response = { + "timestamp": dt_util.utcnow().isoformat(), + "question": message, + "response": response_text, + "error": None + } + + self._history.append(self.last_response) + if len(self._history) > self._max_history_size: + self._history.pop(0) + + self._is_processing = False + self.async_update_listeners() + + return self.last_response + + except Exception as err: + self._error_count += 1 + self._last_error = str(err) + self._performance_metrics["total_errors"] += 1 + + self.last_response = { + "timestamp": dt_util.utcnow().isoformat(), + "question": message, + "response": None, + "error": str(err) + } + + self._is_processing = False + self.async_update_listeners() + raise + async def __aenter__(self): """Async enter.""" await self.async_start() diff --git a/custom_components/ha_text_ai/sensor.py b/custom_components/ha_text_ai/sensor.py index 3e15f1d..70dd153 100644 --- a/custom_components/ha_text_ai/sensor.py +++ b/custom_components/ha_text_ai/sensor.py @@ -80,7 +80,7 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): name=self._name, manufacturer="Community", model=f"{coordinator.model} ({self._config_entry.data.get(CONF_API_PROVIDER, 'Unknown')} provider)", - sw_version=coordinator.api_version, + sw_version=coordinator._api_version, ) @property @@ -95,48 +95,53 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): @property def state(self) -> StateType: """Return the state of the sensor.""" - last_response = self.coordinator.last_response - if last_response and 'timestamp' in last_response: - return dt_util.as_local(last_response['timestamp']) + if self.coordinator.data: + return self.coordinator.data.get("last_update", self._current_state) return self._current_state @property def extra_state_attributes(self) -> Dict[str, Any]: """Return entity specific state attributes.""" attributes = { - ATTR_TOTAL_RESPONSES: len(self.coordinator._history), + ATTR_TOTAL_RESPONSES: len(getattr(self.coordinator, '_history', [])), ATTR_MODEL: self.coordinator.model, ATTR_API_STATUS: self._current_state, ATTR_ERROR_COUNT: self._error_count, ATTR_LAST_ERROR: self._last_error, } - last_response = self.coordinator.last_response - if last_response: - attributes.update({ - ATTR_RESPONSE: last_response.get("response", ""), - ATTR_QUESTION: last_response.get("question", ""), - }) + if self.coordinator.data: + data = self.coordinator.data + if "metrics" in data: + attributes.update({"metrics": data["metrics"]}) + if "status" in data: + attributes[ATTR_API_STATUS] = data["status"] + if "last_response" in data: + last_response = data["last_response"] + if last_response: + attributes.update({ + ATTR_RESPONSE: last_response.get("response", ""), + ATTR_QUESTION: last_response.get("question", ""), + }) return attributes def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" try: - last_response = self.coordinator.last_response - - if last_response: - if last_response.get("error"): - self._current_state = STATE_ERROR - self._error_count += 1 - elif self.coordinator._is_processing: + data = self.coordinator.data + if data: + if data.get("is_processing"): self._current_state = STATE_PROCESSING - elif self.coordinator._is_rate_limited: + elif data.get("is_rate_limited"): self._current_state = STATE_RATE_LIMITED - elif self.coordinator._is_maintenance: + elif data.get("is_maintenance"): self._current_state = STATE_MAINTENANCE else: self._current_state = STATE_READY + + if "metrics" in data: + self._error_count = data["metrics"].get("total_errors", 0) else: self._current_state = STATE_DISCONNECTED