From ad36352fe8752fa68c1c7d8357e3826d2e1a3b22 Mon Sep 17 00:00:00 2001 From: SMKRV Date: Thu, 12 Mar 2026 12:05:43 +0300 Subject: [PATCH] docs: Update README with current model names, remove outdated YAML config section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update recommended models to current versions (Claude 4.6, GPT-5, Gemini 3.1, DeepSeek-V3) - Add Google Gemini to features list - Remove YAML configuration section (integration is config_entry_only) - Remove deleted Api status attribute from documentation - Update conversation_history display count (1 → 5) - Fix FAQ with current model names and token limits - Remove internal spec file --- README.md | 99 +-- docs/specs/2026-03-12-v2.4.0-fix-plan.md | 851 ----------------------- 2 files changed, 19 insertions(+), 931 deletions(-) delete mode 100644 docs/specs/2026-03-12-v2.4.0-fix-plan.md diff --git a/README.md b/README.md index 5db8f03..6c68506 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@

-Transform your smart home experience with powerful AI assistance powered by multiple AI providers including OpenAI GPT, DeepSeek and Anthropic Claude models. Get intelligent responses, automate complex scenarios, and enhance your home automation with advanced natural language processing. +Transform your smart home experience with powerful AI assistance powered by multiple AI providers including OpenAI GPT, Anthropic Claude, DeepSeek and Google Gemini models. Get intelligent responses, automate complex scenarios, and enhance your home automation with advanced natural language processing.

@@ -28,7 +28,7 @@ Transform your smart home experience with powerful AI assistance powered by mult ## 🌟 Features -- 🧠 **Multi-Provider AI Integration**: Support for OpenAI GPT, DeepSeek and Anthropic Claude models +- 🧠 **Multi-Provider AI Integration**: Support for OpenAI GPT, Anthropic Claude, DeepSeek and Google Gemini models - 💬 **Advanced Language Processing**: Context-aware, multi-turn conversations - 📝 **Enhanced Memory Management**: Secure file-based history storage - ⚡ **Performance Optimization**: Efficient token usage and smart rate limiting @@ -44,6 +44,7 @@ Transform your smart home experience with powerful AI assistance powered by mult - Support for OpenAI GPT models - Anthropic Claude integration - DeepSeek integration +- Google Gemini integration - Custom API endpoints - Flexible model selection @@ -135,18 +136,18 @@ Transform your smart home experience with powerful AI assistance powered by mult - **GPT-5** - The latest flagship model, best for complex reasoning - **GPT-5 mini** - A cost-effective and fast model, suitable for most tasks -#### Anthropic Claude Models -- **Claude Opus 4.1** - The most capable model for handling complex tasks -- **Claude Sonnet 4** - Offers a balance between performance and cost -- **Claude Haiku 4** - The fastest and most economical option in the series +#### Anthropic Claude Models +- **Claude Opus 4.6** - The most capable model for handling complex tasks +- **Claude Sonnet 4.6** - Offers a balance between performance and cost +- **Claude Haiku 4.5** - The fastest and most economical option in the series #### DeepSeek Models -- **DeepSeek-V3.1** - A general-purpose model for a wide range of tasks +- **DeepSeek-V3** - A general-purpose model for a wide range of tasks - **DeepSeek-R1** - A specialized model focused on reasoning and coding #### Google Gemini Models -- **Gemini 2.5 Pro & 2.5 Flash** - The newest and most advanced models available -- **Gemini 2.0 Pro & 2.0 Flash** - Previous generation models that are still powerful and efficient +- **Gemini 3.1 Pro** - The newest and most advanced model available +- **Gemini 3.1 Flash Lite** - Fastest and most cost-efficient model for high-volume workloads
🌐 Potentially Compatible Providers @@ -201,7 +202,7 @@ If the integration is not found in the default repository: 1. Download the latest release 2. Extract and copy `custom_components/ha_text_ai` to your `custom_components` directory 3. Restart Home Assistant -4. Add configuration via UI or YAML +4. Add configuration via UI (Settings → Devices & Services → Add Integration) ## ⚙️ Configuration @@ -211,66 +212,7 @@ If the integration is not found in the default repository: 3. Search for "HA Text AI" 4. Follow the configuration steps -
-📦 Via YAML (Advanced) - -### Platform Configuration (Global Settings) - -```yaml -ha_text_ai: - api_provider: openai # Required - api_key: !secret ai_api_key # Required - model: gpt-4o # Strongly recommended - temperature: 0.7 # Optional - max_tokens: 1000 # Optional - request_interval: 1.0 # Optional - api_endpoint: https://api.openai.com/v1 # Required - system_prompt: | # Optional - You are a home automation expert assistant. - Focus on practical and efficient solutions. -``` - -### Sensor Configuration - -```yaml -sensor: - - platform: ha_text_ai - name: "My AI Assistant" # Required, unique identifier - api_provider: openai # Optional (inherits from platform) - model: "gpt-4o" # Optional - temperature: 0.7 # Optional - max_tokens: 1000 # Optional -``` - -### 📋 Configuration Parameters - -#### Platform Configuration - -| Parameter | Type | Required | Default | Description | -|-----------|------|----------|---------|-------------| -| `api_provider` | String | ✅ | - | AI service provider (openai, anthropic, deepseek, gemini) | -| `api_key` | String | ✅ | - | Authentication key for AI service | -| `model` | String | ⚠️ | gpt-4o-mini | Strongly recommended: Specific AI model to use. Default varies by provider | -| `temperature` | Float | ❌ | 0.1 | Response creativity level (0.0-2.0) | -| `max_tokens` | Integer | ❌ | 1000 | Maximum response length | -| `request_interval` | Float | ❌ | 1.0 | Delay between API requests | -| `api_endpoint` | URL | ⚠️ | Provider default | Custom API endpoint | -| `system_prompt` | String | ❌ | - | Default context for AI interactions | -| `max_history_size` | Integer | ❌ | 50 | Maximum number of conversation entries to store | -| `context_messages` | Integer | ❌ | 5 | Number of previous messages to include in context (1-20) | - -#### Sensor Configuration - -| Parameter | Type | Required | Default | Description | -|-----------|------|----------|---------|-------------| -| `platform` | String | ✅ | - | Must be `ha_text_ai` | -| `name` | String | ✅ | - | Unique sensor identifier | -| `api_provider` | String | ❌ | Platform setting | Override global provider | -| `model` | String | ⚠️ | Provider default | Recommended: Override global model (gpt-4o-mini, deepseek-chat, gemini-2.0-flash) | -| `temperature` | Float | ❌ | 0.1 | Override global temperature | -| `max_tokens` | Integer | ❌ | 1000 | Override global max tokens | - -
+> **Note:** This integration is configured exclusively through the UI (config entries). YAML configuration is not supported. ## 🛠️ Available Services @@ -289,7 +231,7 @@ sensor: service: ha_text_ai.ask_question data: question: "What's the optimal temperature for sleeping?" - model: "claude-3.5-sonnet" # optional + model: "claude-sonnet-4-6-20260217" # optional temperature: 0.5 # optional max_tokens: 500 # optional context_messages: 10 #optional, number of previous messages to include in context, default: 5 @@ -305,7 +247,7 @@ response_text: "The optimal sleeping temperature is 65-68°F (18-20°C)..." tokens_used: 150 prompt_tokens: 50 completion_tokens: 100 -model_used: "claude-3.5-sonnet" +model_used: "claude-sonnet-4-6-20260217" instance: "sensor.ha_text_ai_gpt" question: "What's the optimal temperature for sleeping?" timestamp: "2025-02-09T16:57:00.000Z" @@ -563,10 +505,7 @@ automation: #### System Status ```yaml -# Current operational readiness of the AI service API -{{ state_attr('sensor.ha_text_ai_gpt', 'Api status') }} # ready - -# Indicates if a request is currently being processed +# Indicates if a request is currently being processed {{ state_attr('sensor.ha_text_ai_gpt', 'Is processing') }} # false # Shows if the API has hit its request rate limit @@ -608,7 +547,7 @@ automation: # Number of entries in current history file {{ state_attr('sensor.ha_text_ai_gpt', 'History size') }} # 0 -# Last few conversation entries (limited to 1 for performance) +# Last few conversation entries (last 5 for performance) {{ state_attr('sensor.ha_text_ai_gpt', 'conversation_history') }} # [...] ``` @@ -656,7 +595,7 @@ Conversation history stored in `.storage/ha_text_ai_history/` directory: A: OpenAI (GPT models), Anthropic (Claude models), DeepSeek, Google Gemini, and OpenRouter are officially supported, with many other OpenAI-compatible providers working as well. **Q: How can I reduce API costs?** -A: Use gpt-4o-mini or claude-3.5-haiku for most queries, implement caching, and optimize token usage. +A: Use gpt-5-mini or claude-haiku-4-5 for most queries, implement caching, and optimize token usage. **Q: Are there limitations on the number of requests?** A: Depends on your API provider's plan. We recommend monitoring usage and implementing request throttling via `request_interval` configuration. @@ -668,7 +607,7 @@ A: Yes, you can configure custom endpoints and use any compatible model by speci A: Simply change the model parameter in your configuration or service calls to use the desired provider's model. **Q: What are the token limits for different models?** -A: Token limits vary by provider and model. OpenAI's gpt-4o supports up to 128K tokens, Claude 3.5 Sonnet supports up to 200K tokens, while smaller models typically have 8K-32K limits. Check your provider's documentation for specific limits. +A: Token limits vary by provider and model. OpenAI's GPT-5 supports up to 1M context tokens, Claude Opus 4.6 supports up to 1M tokens, Gemini 3.1 Pro supports up to 1M tokens, while smaller models typically have 128K-200K limits. Check your provider's documentation for specific limits. **Q: How do I monitor token usage?** A: Use the sensor attributes like `Total tokens`, `Prompt tokens`, and `Completion tokens` to track usage. You can also create automations to alert you when usage exceeds certain thresholds. @@ -686,7 +625,7 @@ A: History is stored in files under the `.storage/ha_text_ai_history/` directory A: Yes, archived history files are stored with timestamps and can be accessed manually if needed. **Q: How much history is kept?** -A: By default, up to 100 conversations are stored, but this can be configured. Files are automatically rotated when they reach 1MB. +A: By default, up to 50 conversations are stored (max 200), configurable via UI. Files are automatically rotated when they reach 1MB. ## 🤝 Contributing diff --git a/docs/specs/2026-03-12-v2.4.0-fix-plan.md b/docs/specs/2026-03-12-v2.4.0-fix-plan.md deleted file mode 100644 index b75b37e..0000000 --- a/docs/specs/2026-03-12-v2.4.0-fix-plan.md +++ /dev/null @@ -1,851 +0,0 @@ -# ha-text-ai v2.4.0 — Полный план исправлений - -**Дата**: 2026-03-12 -**Версия**: 2.3.0 → 2.4.0 -**Принцип**: Без breaking changes. Один релиз. -**Статус**: В работе - ---- - -## Оглавление - -- [Принципы обратной совместимости](#принципы-обратной-совместимости) -- [Workflow разработки](#workflow-разработки) -- [Фаза 0: Подготовка инфраструктуры](#фаза-0-подготовка-инфраструктуры) -- [Фаза 1: Security Hardening](#фаза-1-security-hardening) -- [Фаза 2: Critical Bug Fixes](#фаза-2-critical-bug-fixes) -- [Фаза 3: Concurrency & State](#фаза-3-concurrency--state) -- [Фаза 4: Dead Code & Deduplication](#фаза-4-dead-code--deduplication) -- [Фаза 5: Coordinator Refactoring](#фаза-5-coordinator-refactoring) -- [Фаза 6: UI/UX & Translations](#фаза-6-uiux--translations) -- [Финал: Full Review + Tests + CI/CD](#финал-full-review--tests--cicd) -- [Трекинг прогресса](#трекинг-прогресса) - ---- - -## Принципы обратной совместимости - -| Что | Правило | -|-----|---------| -| Sensor attributes | Все существующие сохраняются. Новые добавляются. Избыточные остаются для совместимости | -| Service response format | Структура ответа `ask_question`, `get_history` не меняется | -| Service schema | Поля не удаляются, не переименовываются. Новые — только `vol.Optional` | -| Entity ID | Формат `sensor.ha_text_ai_{name}` не меняется | -| Config entry data | Добавление новых ключей OK. Существующие не удаляются. Миграция прозрачная | -| History file format | JSON-структура сохраняется. Новые поля — опциональные | -| `hass.data[DOMAIN]` | Ключи по `entry_id` — не менять | - ---- - -## Workflow разработки - -``` -Для каждой фазы: - 1. Реализация фикса - 2. 4 параллельных review-агента (Code, Security, Architecture, UI/UX) - 3. Документация - 4. Исправление замечаний review - 5. Повторная проверка (4 агента) - 6. Документация - 7. Локальный коммит - -После ВСЕХ фаз: - 1. Полный финальный review (все 4 агента) - 2. Добавление тестов + CI/CD workflow - 3. Финальный коммит - 4. Push -``` - ---- - -## Фаза 0: Подготовка инфраструктуры - -**Цель**: Создать фундамент для остальных фаз без изменения поведения. - -### 0.1. Создать `utils.py` - -**Файл**: `custom_components/ha_text_ai/utils.py` (новый) - -Извлечь из `config_flow.py`: -- Функцию `normalize_name()` (config_flow.py:351-370) -- Вспомогательную функцию `get_file_hash()` из `__init__.py:108-114` -- Хелпер для безопасного логирования (фильтрация API-ключей) - -```python -def safe_log_data(data: dict, sensitive_keys: tuple = (CONF_API_KEY,)) -> dict: - """Filter sensitive keys from data for safe logging.""" - return {k: "***" if k in sensitive_keys else v for k, v in data.items()} -``` - -**Обратная совместимость**: `config_flow.py` сохраняет `normalize_name` как re-export: -```python -from .utils import normalize_name # backward compat -``` -Это позволит `coordinator.py:29` (`from .config_flow import normalize_name`) продолжать работать, пока мы не обновим все импорты. - -### 0.2. Создать `providers.py` - -**Файл**: `custom_components/ha_text_ai/providers.py` (новый) - -Единый реестр провайдеров вместо дублирования dispatch-таблиц в 4+ местах: - -```python -PROVIDER_REGISTRY = { - API_PROVIDER_OPENAI: { - "default_model": DEFAULT_MODEL_OPENAI, - "default_endpoint": DEFAULT_ENDPOINT_OPENAI, - "auth_header": "Authorization", - "auth_prefix": "Bearer ", - "check_path": "/models", - }, - API_PROVIDER_ANTHROPIC: { ... }, - API_PROVIDER_DEEPSEEK: { ... }, - API_PROVIDER_GEMINI: { ... }, -} - -def get_provider_config(provider: str) -> dict: ... -def get_default_endpoint(provider: str) -> str: ... -def get_default_model(provider: str) -> str: ... -def build_auth_headers(provider: str, api_key: str) -> dict: ... -``` - -### 0.3. Обновить импорты - -Во всех файлах заменить: -- `from .config_flow import normalize_name` → `from .utils import normalize_name` -- Inline dispatch-таблицы → вызовы `providers.py` - -**Коммит**: `refactor: Extract utils.py and providers.py for code deduplication` - ---- - -## Фаза 1: Security Hardening - -**Цель**: Устранить все уязвимости безопасности. - -### 1.1. Убрать логирование API-ключей [CRITICAL] - -**Файлы и строки**: -- `__init__.py:312` — `_LOGGER.debug(f"Setting up HA Text AI entry: {entry.data}")` -- `config_flow.py:160` — `_LOGGER.debug(f"Provider step input data: {user_input}")` -- `config_flow.py:465` — `_LOGGER.debug(f"Creating config entry with data: {entry_data}")` -- `sensor.py:116` — `_LOGGER.debug(f"Initializing sensor with config entry: {config_entry.data}")` - -**Исправление**: Заменить на `safe_log_data()` из `utils.py`: -```python -_LOGGER.debug("Setting up HA Text AI entry: %s", safe_log_data(entry.data)) -``` - -Заодно перевести все `_LOGGER` вызовы с f-string на `%s`-форматирование (lazy evaluation). - -**Масштаб**: Все файлы, все `_LOGGER.debug(f"...")` → `_LOGGER.debug("...", arg)`. - -### 1.2. Валидация endpoint — HTTPS-only + защита от SSRF [HIGH] - -**Файлы**: `utils.py` (новая функция), `config_flow.py`, `__init__.py` - -Добавить в `utils.py`: -```python -from urllib.parse import urlparse -import ipaddress - -def validate_endpoint(url: str) -> tuple[bool, str]: - """Validate API endpoint URL. Returns (is_valid, error_message).""" - parsed = urlparse(url) - if parsed.scheme not in ("https",): - return False, "only_https_supported" - hostname = parsed.hostname - if not hostname: - return False, "invalid_url" - try: - ip = ipaddress.ip_address(hostname) - if ip.is_private or ip.is_loopback or ip.is_link_local: - return False, "private_ip_not_allowed" - except ValueError: - pass # hostname is a domain name, not IP — OK - return True, "" -``` - -Применить в: -- `config_flow.py:async_step_provider` — при вводе endpoint -- `config_flow.py:OptionsFlowHandler` — при смене endpoint -- `__init__.py:async_setup_entry` — перед созданием API client - -**Обратная совместимость**: Существующие записи с HTTP endpoints продолжат работать (валидация только при создании/изменении). Добавить warning в лог при загрузке существующего HTTP endpoint. - -### 1.3. Не показывать API-ключ в формах [HIGH] - -**Файлы**: `config_flow.py:648-650, 224, 286, 329` - -**Исправление**: -- В `_get_settings_schema` и во всех error re-display формах: НЕ передавать `default=data.get(CONF_API_KEY)`. -- Вместо `vol.Required(CONF_API_KEY, default=...)` использовать `vol.Optional(CONF_API_KEY, description={"suggested_value": ""})`. -- Если поле пустое — сохранять существующий ключ. - -### 1.4. Требовать повторный ввод ключа при смене endpoint [HIGH] - -**Файл**: `config_flow.py:OptionsFlowHandler.async_step_settings` - -При обнаружении изменения endpoint: -- Очистить поле API key -- Показать предупреждение в описании поля -- Не отправлять старый ключ на новый endpoint при валидации - -### 1.5. Убрать history_path из атрибутов сенсора [MEDIUM] - -**Файл**: `coordinator.py:_get_limited_history` - -Удалить `"history_path": self._history_file` из возвращаемого dict. Это внутренний путь FS. - -### 1.6. Санитизировать ошибки в service response [MEDIUM] - -**Файл**: `__init__.py:161` - -Заменить `"error": str(err)` на `"error": "Request failed"`. Полную ошибку логировать через `_LOGGER.error`. - -### 1.7. Добавить лимит длины для question и json_schema [LOW] - -**Файл**: `__init__.py:73,80` - -```python -vol.Required("question"): vol.All(cv.string, vol.Length(max=100000)), -vol.Optional("json_schema"): vol.All(cv.string, vol.Length(max=50000)), -``` - -### 1.8. Валидация Gemini API key [LOW] - -**Файл**: `config_flow.py:263-278`, `__init__.py:279-285` - -Для Gemini: выполнять реальный API-вызов `models.list()` вместо проверки "непустая строка". - -**Коммит**: `fix: Security hardening — credential protection, SSRF prevention, input validation` - ---- - -## Фаза 2: Critical Bug Fixes - -**Цель**: Исправить баги, приводящие к потере данных и некорректному поведению. - -### 2.1. Исправить инициализацию координатора [CRITICAL] - -**Файл**: `coordinator.py:177-186` - -**Было**: 5 fire-and-forget `async_create_task` в `__init__` + `_history_file` назначается после. - -**Станет**: -1. Убрать все `async_create_task` из `__init__` -2. Переместить `self._history_file` назначение ПЕРЕД задачами -3. Создать метод `async_initialize()`: -```python -async def async_initialize(self) -> None: - """Initialize coordinator: directories, history, metrics. Must be awaited.""" - await self._create_history_dir() - await self._check_history_directory() - await self._initialize_metrics() - await self.async_initialize_history_file() - await self._migrate_history_from_txt_to_json() -``` -4. В `__init__.py:async_setup_entry` вызывать `await coordinator.async_initialize()` после конструктора. - -### 2.2. Удалить деструктивный цикл `async_set` [CRITICAL] - -**Файл**: `coordinator.py:778-781` - -Удалить полностью цикл: -```python -for entity_id in self.hass.states.async_entity_ids(): - if entity_id.startswith(entity_id_base): - self.hass.states.async_set(entity_id, self._get_current_state()) -``` - -`async_request_refresh()` на строке 775 уже обновляет состояние через координатор → `_handle_coordinator_update` → `async_write_ha_state()`. - -### 2.3. Исправить `temperature=0` [HIGH] - -**Файл**: `coordinator.py:870-873` - -Заменить: -```python -temp_temperature = temperature or self.temperature -temp_max_tokens = max_tokens or self.max_tokens -``` -На: -```python -temp_temperature = temperature if temperature is not None else self.temperature -temp_max_tokens = max_tokens if max_tokens is not None else self.max_tokens -temp_context = context_messages if context_messages is not None else self.context_messages -temp_system = system_prompt if system_prompt is not None else self._system_prompt -``` - -**Файл**: `__init__.py:76` - -Заменить `cv.positive_float` на `vol.All(vol.Coerce(float), vol.Range(min=0.0, max=2.0))` для temperature. - -### 2.4. Исправить retry loop — не ретраить нетранзиентные ошибки [HIGH] - -**Файл**: `api_client.py:86-122` - -```python -# Было: except Exception ловит HomeAssistantError (4xx ответы) и ретраит -# Станет: -for attempt in range(self.retry_count): - try: - async with self.session.post(...) as response: - if response.ok: - return await response.json() - error_data = {} - try: - error_data = await response.json() - except Exception: - error_data = {"raw": await response.text()} - if response.status == 429: # rate limit — retry - await asyncio.sleep(attempt + 1) - continue - if response.status >= 400: # client/server error — don't retry - raise HomeAssistantError(f"API error: {response.status}") - except (asyncio.TimeoutError, aiohttp.ClientError) as err: - if attempt == self.retry_count - 1: - raise HomeAssistantError(f"Connection failed: {err}") from err - await asyncio.sleep(attempt + 1) -``` - -### 2.5. Починить ротацию истории [HIGH] - -**Файл**: `coordinator.py:588-592` - -`_rotate_history_files` — `async def`, но вызывается через `async_add_executor_job` (запускает sync в потоке → получает coroutine, не результат). - -**Исправление**: Заменить: -```python -await self.hass.async_add_executor_job(self._rotate_history_files) -``` -На: -```python -await self._rotate_history_files() -``` - -Или сделать `_rotate_history_files` синхронной (предпочтительнее — она делает файловые операции). - -### 2.6. Исправить `_is_rate_limited` — сбрасывать после успеха [HIGH] - -**Файл**: `coordinator.py` - -В `async_process_message` после успешного API-вызова: -```python -self._is_rate_limited = False -self.endpoint_status = "ready" -self._error_count = 0 -``` - -### 2.7. `ConnectionError` не должен ставить `is_rate_limited` [HIGH] - -**Файл**: `coordinator.py:361-364` - -Убрать `"is_rate_limited": True` из маппинга `ConnectionError`. - -### 2.8. Исправить `async_ask_question` — propagation ошибок [MEDIUM] - -**Файл**: `coordinator.py:815-849` - -`async_ask_question` — бессмысленная обёртка над `async_process_question`. Убрать лишний уровень индирекции, или добавить полезную логику (валидацию входных параметров). - -**Файл**: `__init__.py:148-162` - -Сервис `ask_question` глотает все ошибки. Добавить `raise HomeAssistantError` для критических ошибок, сохраняя `success: False` в response data для совместимости: -```python -except Exception as err: - _LOGGER.error("ask_question failed: %s", err) - response = {"success": False, "error": "Request failed", ...} - # НЕ raise — для обратной совместимости. Ошибка в response data. - return response -``` - -**Коммит**: `fix: Critical bugs — init race condition, data loss, temperature=0, retry logic, history rotation` - ---- - -## Фаза 3: Concurrency & State - -**Цель**: Устранить race conditions и проблемы управления состоянием. - -### 3.1. Добавить `asyncio.Lock` для конкурентных запросов [MEDIUM] - -**Файл**: `coordinator.py:__init__` - -```python -self._request_lock = asyncio.Lock() -``` - -В `async_process_question`: -```python -async with self._request_lock: - # ... existing logic -``` - -### 3.2. Исправить Semaphore — убрать или сделать instance-level [MEDIUM] - -**Файл**: `coordinator.py:668` - -Убрать `async with asyncio.Semaphore(1):` — `DataUpdateCoordinator` уже сериализует `_async_update_data`. - -### 3.3. Исправить `async_shutdown` — правильный ключ [HIGH] - -**Файл**: `coordinator.py:1159` - -Заменить `self.hass.data[DOMAIN].pop(self.instance_name, None)` на: -```python -# Cleanup handled by async_unload_entry in __init__.py -# Remove this line entirely — it uses wrong key (instance_name vs entry_id) -``` - -### 3.4. Исправить double timeout в `_make_request` [MEDIUM] - -**Файл**: `api_client.py:98-103` - -Убрать `async with timeout(self.api_timeout):` — `aiohttp ClientTimeout` достаточно. Убрать `from async_timeout import timeout`. - -### 3.5. Кэшировать Gemini client [MEDIUM] - -**Файл**: `api_client.py` - -```python -def __init__(self, ...): - ... - self._gemini_client = None - -def _get_gemini_client(self, api_key): - if self._gemini_client is None: - self._gemini_client = genai.Client(api_key=api_key, ...) - return self._gemini_client -``` - -### 3.6. Исправить `get_history` — не мутировать оригинальные записи [MEDIUM] - -**Файл**: `coordinator.py:1118-1125` - -```python -# Было: entry["metadata"] = {...} # мутирует оригинал -# Станет: -import copy -enriched = copy.deepcopy(entry) -enriched["metadata"] = {...} -``` - -### 3.7. Персистировать system_prompt [LOW] - -**Файл**: `coordinator.py` - -Сохранять `_system_prompt` в файл `.storage/ha_text_ai_system_prompt_{name}.txt`. Загружать при инициализации. При reload/restart — восстанавливать. - -### 3.8. Исправить `os.rename` в async контексте [MEDIUM] - -**Файл**: `coordinator.py:1047` - -```python -# Было: os.rename(old_history_file, backup_file) -# Станет: -await self.hass.async_add_executor_job(os.rename, old_history_file, backup_file) -``` - -**Коммит**: `fix: Concurrency — request locking, state management, Gemini client caching, system prompt persistence` - ---- - -## Фаза 4: Dead Code & Deduplication - -**Цель**: Удалить мёртвый код и устранить дублирование. - -### 4.1. Удалить мёртвый код - -| Файл | Что удалить | Строки | -|------|-------------|--------| -| `coordinator.py` | `_check_memory_available()` + `import psutil` | 1138-1154, 17 | -| `coordinator.py` | `_sync_write_history_entry()` | 535-586 | -| `coordinator.py` | `AsyncFileHandler` class (заменить на прямой `aiofiles.open`) | 51-63 | -| `api_client.py` | `check_connection()` (всегда broken) | 326-... | -| `api_client.py` | `self._closed` flag (никто не проверяет) | в shutdown() | -| `api_client.py` | `import HomeAssistant` (не используется) | 16 | -| `api_client.py` | `import datetime, timedelta` (не используются) | 14 | -| `coordinator.py` | `import re` (не используется) | 18 | -| `__init__.py` | `import TypeVar` + `ConfigType` (не нужен) | 16, 69 | -| `__init__.py` | `import timedelta` (не используется) | 15 | - -### 4.2. Консолидировать schema определения - -**Файл**: `const.py:188-256` - -Удалить мёртвые schema: -- `SERVICE_SCHEMA_ASK_QUESTION` (неиспользуемый, конфликтует с `__init__.py`) -- `SERVICE_SCHEMA_SET_SYSTEM_PROMPT` (дублирует `__init__.py`) -- `SERVICE_SCHEMA_GET_HISTORY` (дублирует `__init__.py`) -- `CONFIG_SCHEMA` (не используется) - -Оставить единственное определение в `__init__.py`. - -### 4.3. Удалить неиспользуемые зависимости из `manifest.json` - -**Файл**: `manifest.json` - -Удалить: -- `anthropic>=0.8.0` — не импортируется (API через `aiohttp`) -- `openai>=1.12.0` — не импортируется (API через `aiohttp`) -- `certifi>=2024.2.2` — уже есть в HA -- `async-timeout>=4.0.0` — deprecated, заменяем на `asyncio.timeout` - -Обновить: -- `aiohttp>=3.9.4` (security fix: CVE-2024-23334, CVE-2024-23829) - -### 4.4. Использовать `providers.py` вместо inline dispatch - -Заменить во всех файлах inline-таблицы `default_endpoint`/`default_model`/headers: - -**`__init__.py:326-362`**: -```python -# Было: 4 if/elif для endpoint + 4 if/elif для model + 4 if/elif для headers -# Станет: -from .providers import get_provider_config, build_auth_headers -config = get_provider_config(provider) -endpoint = entry_data.get(CONF_API_ENDPOINT) or config["default_endpoint"] -headers = build_auth_headers(provider, api_key) -``` - -**`config_flow.py:104-117, 487-502`**: аналогично. - -### 4.5. Консолидировать `_async_validate_api` - -**Файлы**: `config_flow.py` — два идентичных метода в `HATextAIConfigFlow` (372-407) и `OptionsFlowHandler` (517-549). - -Извлечь в `utils.py`: -```python -async def validate_api_connection(hass, provider, endpoint, api_key) -> tuple[bool, str]: -``` - -### 4.6. Унифицировать timeout - -Заменить все `from async_timeout import timeout` на `from asyncio import timeout` (Python 3.12+, HA 2024.12.0 требует). - -**Файлы**: `api_client.py:3`, `__init__.py:19` - -**Коммит**: `refactor: Remove dead code, consolidate schemas, unify provider dispatch, clean dependencies` - ---- - -## Фаза 5: Coordinator Refactoring - -**Цель**: Разбить god object `coordinator.py` (~1160 строк) на модули с чёткими ответственностями. - -### 5.1. Создать `history.py` - -**Файл**: `custom_components/ha_text_ai/history.py` (новый) - -Извлечь из `coordinator.py`: -- `_create_history_dir()` -- `_check_history_directory()` -- `async_initialize_history_file()` -- `_migrate_history_from_txt_to_json()` -- `_write_history_entry()` -- `_rotate_history()` / `_rotate_history_files()` -- `async_clear_history()` -- `async_get_history()` -- `_update_history()` -- `_get_limited_history()` -- `_conversation_history` list -- `_request_lock` (для записи в историю) - -Класс: -```python -class HistoryManager: - def __init__(self, hass, instance_name, normalized_name, history_dir, max_history_size): - ... - async def async_initialize(self): ... - async def write_entry(self, entry): ... - async def get_history(self, limit, filter_model, ...): ... - async def clear(self): ... - def get_limited(self, max_entries): ... - @property - def conversation_history(self) -> list: ... - @property - def size(self) -> int: ... -``` - -### 5.2. Создать `metrics.py` - -**Файл**: `custom_components/ha_text_ai/metrics.py` (новый) - -Извлечь из `coordinator.py`: -- `_initialize_metrics()` -- `_update_metrics()` -- `_save_metrics()` / `_load_metrics()` -- `_metrics` dict -- `_total_tokens`, `_prompt_tokens`, `_completion_tokens` -- `_successful_requests`, `_failed_requests` -- `_average_latency`, `_max_latency`, `_min_latency` - -Класс: -```python -class MetricsTracker: - def __init__(self, hass, normalized_name, storage_dir): - ... - async def async_initialize(self): ... - def update(self, tokens_used, prompt_tokens, completion_tokens, latency): ... - async def save(self): ... - @property - def summary(self) -> dict: ... -``` - -### 5.3. Упростить `coordinator.py` - -После извлечения `coordinator.py` сократится с ~1160 до ~400-500 строк: - -```python -class HATextAICoordinator(DataUpdateCoordinator): - def __init__(self, hass, client, config, ...): - self.history = HistoryManager(...) - self.metrics = MetricsTracker(...) - ... - - async def async_initialize(self): - await self.history.async_initialize() - await self.metrics.async_initialize() - - async def async_process_question(self, question, **kwargs): - async with self._request_lock: - ... - response = await self.client.create(...) - self.metrics.update(...) - await self.history.write_entry(...) - return response - - async def _async_update_data(self): - ... -``` - -### 5.4. Обновить `sensor.py` - -`sensor.py` читает данные из координатора. Обновить доступ: -```python -# Было: self.coordinator._conversation_history -# Станет: self.coordinator.history.conversation_history -``` - -### 5.5. Добавить лимит архивов истории - -**Файл**: `history.py` (в рамках ротации) - -При ротации — хранить максимум 5 архивных файлов. Удалять самые старые. - -**Коммит**: `refactor: Split coordinator into coordinator, history, and metrics modules` - ---- - -## Фаза 6: UI/UX & Translations - -**Цель**: Исправить проблемы пользовательского интерфейса и переводов. - -### 6.1. Исправить дублированный JSON-ключ `"provider"` [CRITICAL] - -**Файлы**: Все 8 файлов в `translations/` - -В каждом файле под `config.step` есть два ключа `"provider"`. Объединить в один с правильным содержимым (title + data из второго, т.к. первый перезаписывается). - -### 6.2. Добавить `strings.json` [LOW] - -**Файл**: `custom_components/ha_text_ai/strings.json` (новый) - -Скопировать `translations/en.json` → `strings.json` (HA convention: `strings.json` = authoritative English source). - -### 6.3. Извлечь form schema builder для config flow [HIGH] - -**Файл**: `config_flow.py` - -Создать `_build_provider_schema(user_input, provider, errors)` по образцу `_get_settings_schema` из `OptionsFlowHandler`. Использовать единый метод во всех 5 местах re-display формы. Все поля всегда присутствуют. - -### 6.4. Использовать `NumberSelector` для числовых полей [MEDIUM] - -**Файл**: `config_flow.py` - -Для temperature, max_tokens, request_interval, api_timeout, context_messages, max_history_size: - -```python -vol.Required(CONF_TEMPERATURE, default=DEFAULT_TEMPERATURE): selector.NumberSelector( - selector.NumberSelectorConfig(min=0.0, max=2.0, step=0.1, mode="slider") -), -``` - -### 6.5. Выровнять переводы с кодом - -**Файлы**: Все `translations/*.json` - -| Действие | Что | -|----------|-----| -| Удалить | Мёртвые error keys: `invalid_characters`, `name_too_long`, `history_storage_error`, `history_rotation_error`, `history_file_access_error`, `invalid_model`, `rate_limit`, `context_length` и др. | -| Удалить | Мёртвые state keys: `retrying`, `queued` | -| Добавить | Недостающие attribute keys для реальных атрибутов | -| Оставить | Все существующие используемые ключи | - -### 6.6. Исправить ошибки в переводах - -| Язык | Проблема | Исправление | -|------|----------|-------------| -| `it.json` | "Istanze" (мн.ч.) → "Istanza" (ед.ч.) | Заменить все вхождения | -| `hi.json` | "instance" = "उदाहरण" (example) | Заменить на "इंस्टैंस" | -| `sr.json` | "disconnected" = "Прекључено" (switched) | Заменить на "Искључено" | -| `de.json` | Опции селектора не переведены | Перевести: "OpenAI (kompatibel)" и т.д. | - -### 6.7. Прочие UI-исправления - -| # | Что | Файл | -|---|-----|------| -| A | `PLATFORMS: list[Platform] = [Platform.SENSOR]` | `const.py:20` | -| B | `supports_response=SupportsResponse.OPTIONAL` | `__init__.py:203` | -| C | `manufacturer="SMKRV"` вместо `"Community"` | `sensor.py:157` | -| D | `datetime` selector для `start_date` в services | `services.yaml:146-149` | -| E | Выровнять temperature default: 0.1 везде | `services.yaml:63` | -| F | Убрать "This service now returns..." из description | `services.yaml:6-7` | -| G | Убрать пустые массивы из manifest (`bluetooth`, `mqtt` и т.д.) | `manifest.json` | -| H | Убрать нестандартное копирование иконок в `www/` | `__init__.py:228-272` | - -**Пункт H — детали**: Современные HA + HACS используют `icons/` directory напрямую. Удалить `_copy_icon_to_www()` и весь связанный код. Существующие файлы в `www/ha_text_ai/` не трогаем (пользователь может удалить сам). - -### 6.8. Исправить 404 как успешную валидацию API - -**Файлы**: `__init__.py:295`, `config_flow.py:399` - -Только 200 = успех. 404 → `cannot_connect`. - -**Коммит**: `fix: UI/UX — translations, config flow forms, sensor attributes, services.yaml alignment` - ---- - -## Финал: Full Review + Tests + CI/CD - -### F.1. Полный финальный review - -Запуск всех 4 review-агентов на весь codebase: -1. Code Review -2. Security Review -3. Architectural Review -4. UI/UX Review - -Исправить все найденные проблемы. Повторить до чистого прохода. - -### F.2. Добавить тесты - -**Файлы**: `tests/` (новая директория) - -Минимальный набор тестов: - -``` -tests/ -├── conftest.py # HA test fixtures -├── test_config_flow.py # Config flow + options flow -├── test_api_client.py # API client per provider -├── test_coordinator.py # Coordinator logic -├── test_sensor.py # Sensor entity -├── test_utils.py # Utility functions -├── test_history.py # History manager -├── test_metrics.py # Metrics tracker -└── test_providers.py # Provider registry -``` - -Ключевые тесты: -- Config flow: создание entry для каждого провайдера, validation errors, options flow -- API client: успешный ответ, retry на 429, НЕ retry на 400, timeout -- Coordinator: `temperature=0` работает, concurrent requests, history write/read -- Security: endpoint validation (HTTPS-only, no private IPs), API key не в логах -- History: write, read, rotate, archive cleanup -- Translations: все файлы имеют одинаковые ключи - -### F.3. CI/CD workflow для тестов - -**Файл**: `.github/workflows/tests.yaml` (новый) - -```yaml -name: Tests -on: - push: - branches: [main, dev] - pull_request: - branches: [main] -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Install dependencies - run: | - pip install pytest pytest-asyncio pytest-homeassistant-custom-component - pip install -r requirements_test.txt - - name: Run tests - run: pytest tests/ -v -``` - -### F.4. Обновить version в manifest.json - -`"version": "2.3.0"` → `"version": "2.4.0"` - -### F.5. Финальные коммиты - -``` -test: Add comprehensive test suite for all components -chore: Add test workflow to CI/CD -chore: Bump version to 2.4.0 -``` - -### F.6. Push - -Только после успешного прохождения всех проверок. - ---- - -## Трекинг прогресса - -| Фаза | Статус | Дата начала | Дата завершения | Коммит | -|------|--------|-------------|-----------------|--------| -| 0. Подготовка | [x] | 2026-03-12 | 2026-03-12 | e7c8b22+ | -| 1. Security | [ ] | | | | -| 2. Critical Bugs | [ ] | | | | -| 3. Concurrency | [ ] | | | | -| 4. Dead Code | [ ] | | | | -| 5. Coordinator | [ ] | | | | -| 6. UI/UX | [ ] | | | | -| F. Final Review | [ ] | | | | -| F. Tests | [ ] | | | | -| F. CI/CD | [ ] | | | | -| F. Push | [ ] | | | | - ---- - -## Новые файлы (итого) - -``` -custom_components/ha_text_ai/ -├── utils.py (Phase 0) -├── providers.py (Phase 0) -├── history.py (Phase 5) -├── metrics.py (Phase 5) -├── strings.json (Phase 6) -tests/ -├── conftest.py (Final) -├── test_*.py (Final) -.github/workflows/ -├── tests.yaml (Final) -``` - -## Изменяемые файлы (все фазы) - -``` -custom_components/ha_text_ai/ -├── __init__.py (Phases 1,2,4,6) -├── api_client.py (Phases 2,3,4) -├── config_flow.py (Phases 0,1,4,6) -├── const.py (Phases 4,6) -├── coordinator.py (Phases 1,2,3,4,5) -├── sensor.py (Phases 1,5,6) -├── manifest.json (Phases 4,6,Final) -├── services.yaml (Phase 6) -├── translations/*.json (Phase 6) -```