Release v2.0.0

This commit is contained in:
SMKRV
2024-11-21 17:18:13 +03:00
parent 9f1ea70c9f
commit 9306c12fcd
+96 -55
View File
@@ -129,17 +129,20 @@ class HATextAICoordinator(DataUpdateCoordinator):
async def _async_update_data(self) -> Dict[str, Any]: async def _async_update_data(self) -> Dict[str, Any]:
"""Update data via API.""" """Update data via API."""
if not self._is_ready:
return self._responses
if self._question_queue.empty(): if self._question_queue.empty():
return self._responses return self._responses
try: try:
async with async_timeout.timeout(DEFAULT_TIMEOUT): async with async_timeout.timeout(DEFAULT_TIMEOUT):
self._is_processing = True self._is_processing = True
priority, question_data = await self._question_queue.get()
question = question_data["question"]
params = question_data["params"]
try: try:
priority, question_data = await self._question_queue.get()
question = question_data["question"]
params = question_data["params"]
response_data = await self._make_api_call( response_data = await self._make_api_call(
question, question,
model=params.get("model"), model=params.get("model"),
@@ -148,42 +151,63 @@ class HATextAICoordinator(DataUpdateCoordinator):
system_prompt=params.get("system_prompt") system_prompt=params.get("system_prompt")
) )
self._update_metrics(response_data) await self._handle_successful_response(
self._responses[question] = { question, response_data, params, priority
"question": question, )
"response": response_data["response"],
"error": None,
"timestamp": dt_util.utcnow(),
"model": response_data["model"],
"temperature": params.get("temperature", self.temperature),
"max_tokens": params.get("max_tokens", self.max_tokens),
"response_time": response_data.get("response_time"),
"tokens": response_data.get("tokens", 0),
"priority": priority
}
self._error_count = 0
self._is_ready = True
self._endpoint_status = "connected"
self._request_count += 1
self._tokens_used += response_data.get("tokens", 0)
self._last_request_time = time.time()
# Fire event for successful response except asyncio.TimeoutError:
self.hass.bus.async_fire(f"{DOMAIN}_response_received", { _LOGGER.error("Timeout while processing API request")
"question": question, await self._handle_api_error(question, "Request timeout")
"model": response_data["model"],
"tokens": response_data.get("tokens", 0)
})
_LOGGER.debug("Response received for question: %s", question) except KeyError as ke:
_LOGGER.error("Invalid question data format: %s", str(ke))
await self._handle_api_error(question, f"Invalid data format: {ke}")
except Exception as err: except Exception as err:
_LOGGER.error("Error processing question: %s", str(err))
await self._handle_api_error(question, err) await self._handle_api_error(question, err)
finally: finally:
self._is_processing = False self._is_processing = False
self._question_queue.task_done() self._question_queue.task_done()
return self._responses except Exception as e:
_LOGGER.error("Critical error in _async_update_data: %s", str(e))
self._is_ready = False
self._endpoint_status = "error"
return self._responses
async def _handle_successful_response(self, question, response_data, params, priority):
"""Handle successful API response."""
self._responses[question] = {
"question": question,
"response": response_data["response"],
"error": None,
"timestamp": dt_util.utcnow(),
"model": response_data["model"],
"temperature": params.get("temperature", self.temperature),
"max_tokens": params.get("max_tokens", self.max_tokens),
"response_time": response_data.get("response_time"),
"tokens": response_data.get("tokens", 0),
"priority": priority
}
self._error_count = 0
self._is_ready = True
self._endpoint_status = "connected"
self._request_count += 1
self._tokens_used += response_data.get("tokens", 0)
self._last_request_time = time.time()
# Fire event for successful response
self.hass.bus.async_fire(f"{DOMAIN}_response_received", {
"question": question,
"model": response_data["model"],
"tokens": response_data.get("tokens", 0)
})
_LOGGER.debug("Response received for question: %s", question)
except asyncio.TimeoutError as err: except asyncio.TimeoutError as err:
_LOGGER.error("Timeout while processing question") _LOGGER.error("Timeout while processing question")
@@ -344,24 +368,34 @@ class HATextAICoordinator(DataUpdateCoordinator):
max_tokens=max_tokens if max_tokens is not None else self.max_tokens, max_tokens=max_tokens if max_tokens is not None else self.max_tokens,
) )
_LOGGER.debug("Received API response: %s", completion) _LOGGER.debug("Raw API response: %s", completion)
if not completion or not hasattr(completion, 'choices') or not completion.choices: if completion is None:
raise ValueError("Invalid or empty response from API") raise ValueError("Received null response from API")
if not hasattr(completion, 'choices') or not completion.choices:
raise ValueError("No choices in API response")
if not hasattr(completion.choices[0], 'message'):
raise ValueError("No message in API response choice")
# Безопасное извлечение сообщения
message = completion.choices[0].message message = completion.choices[0].message
if not message or not hasattr(message, 'content'): if not hasattr(message, 'content'):
raise ValueError("No content in API response message") raise ValueError("No content in message")
response_text = message.content
return { return {
"response": message.content, "response": response_text,
"model": getattr(completion, 'model', model or self.model), "model": getattr(completion, 'model', model or self.model),
"tokens": completion.usage.total_tokens if hasattr(completion, 'usage') else 0 "tokens": completion.usage.total_tokens if hasattr(completion, 'usage') else 0
} }
except Exception as e: except Exception as e:
_LOGGER.error("OpenAI API call error: %s", str(e)) _LOGGER.error("OpenAI API call error: %s", str(e))
# Добавим более подробное логирование
if completion:
_LOGGER.debug("Failed response structure: %s", str(completion))
raise raise
async def async_ask_question( async def async_ask_question(
@@ -374,29 +408,36 @@ class HATextAICoordinator(DataUpdateCoordinator):
priority: bool = False priority: bool = False
) -> None: ) -> None:
"""Add question to queue with priority support.""" """Add question to queue with priority support."""
if not self._is_ready and self._error_count >= self._MAX_ERRORS:
_LOGGER.warning("Coordinator is not ready due to previous errors")
return
question_data = {
"question": question,
"params": {
"system_prompt": system_prompt,
"model": model,
"temperature": temperature,
"max_tokens": max_tokens
}
}
# Priority: 0 for high priority, 1 for normal
priority_level = 0 if priority else 1
try: try:
if not self._is_ready and self._error_count >= self._MAX_ERRORS:
_LOGGER.warning("Coordinator is not ready due to previous errors")
return
_LOGGER.debug("Processing question: %s with model: %s", question, model or self.model)
question_data = {
"question": question,
"params": {
"system_prompt": system_prompt,
"model": model,
"temperature": temperature,
"max_tokens": max_tokens
}
}
# Priority: 0 for high priority, 1 for normal
priority_level = 0 if priority else 1
await self._question_queue.put((priority_level, question_data)) await self._question_queue.put((priority_level, question_data))
_LOGGER.debug("Question added to queue with priority %d", priority_level)
await self.async_refresh() await self.async_refresh()
except asyncio.QueueFull: except asyncio.QueueFull:
_LOGGER.error("Question queue is full. Try again later.") _LOGGER.error("Question queue is full. Try again later.")
raise RuntimeError("Queue is full") raise RuntimeError("Queue is full")
except Exception as e:
_LOGGER.error("Error in async_ask_question: %s", str(e))
raise
async def async_shutdown(self) -> None: async def async_shutdown(self) -> None:
"""Shutdown the coordinator.""" """Shutdown the coordinator."""