From 30aa894634c43774744a4464747ea999934b0899 Mon Sep 17 00:00:00 2001 From: SMKRV Date: Tue, 19 Nov 2024 15:10:06 +0300 Subject: [PATCH] Release v1.0.6 --- custom_components/ha_text_ai/config_flow.py | 46 +++++++++++------- .../ha_text_ai/translations/ru.json | 9 +++- ha_text_ai.zip | Bin 16876 -> 16972 bytes 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/custom_components/ha_text_ai/config_flow.py b/custom_components/ha_text_ai/config_flow.py index d23d0e6..195e4ee 100644 --- a/custom_components/ha_text_ai/config_flow.py +++ b/custom_components/ha_text_ai/config_flow.py @@ -40,29 +40,22 @@ STEP_USER_DATA_SCHEMA = vol.Schema({ default=DEFAULT_TEMPERATURE ): vol.All( vol.Coerce(float), - vol.Range(min=0, max=2), - msg="Temperature must be between 0 and 2" + vol.Range(min=0, max=2) ), vol.Optional( CONF_MAX_TOKENS, default=DEFAULT_MAX_TOKENS ): vol.All( vol.Coerce(int), - vol.Range(min=1, max=4096), - msg="Max tokens must be between 1 and 4096" - ), - vol.Optional(CONF_API_ENDPOINT, default=DEFAULT_API_ENDPOINT): vol.All( - str, - vol.Url(), # Заменено с URL на Url - msg="Must be a valid URL" + vol.Range(min=1, max=4096) ), + vol.Optional(CONF_API_ENDPOINT, default=DEFAULT_API_ENDPOINT): str, vol.Optional( CONF_REQUEST_INTERVAL, default=DEFAULT_REQUEST_INTERVAL ): vol.All( vol.Coerce(float), - vol.Range(min=0.1), - msg="Request interval must be at least 0.1 seconds" + vol.Range(min=0.1) ), }) @@ -172,12 +165,32 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if user_input is not None: try: + # Validate URL format + endpoint = user_input.get(CONF_API_ENDPOINT, DEFAULT_API_ENDPOINT) + try: + result = urlparse(endpoint) + if not all([result.scheme, result.netloc]): + errors["base"] = "invalid_url_format" + return self.async_show_form( + step_id="user", + data_schema=STEP_USER_DATA_SCHEMA, + errors=errors + ) + except Exception as e: + _LOGGER.error("URL parsing error: %s", str(e)) + errors["base"] = "invalid_url_format" + return self.async_show_form( + step_id="user", + data_schema=STEP_USER_DATA_SCHEMA, + errors=errors + ) + # Validate input data user_input = STEP_USER_DATA_SCHEMA(user_input) is_valid, error_code, available_models = await validate_api_connection( user_input[CONF_API_KEY], - user_input.get(CONF_API_ENDPOINT, DEFAULT_API_ENDPOINT), + endpoint, user_input[CONF_MODEL] ) @@ -244,8 +257,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow): description={"suggested_value": DEFAULT_TEMPERATURE}, ): vol.All( vol.Coerce(float), - vol.Range(min=0, max=2), - msg="Temperature must be between 0 and 2" + vol.Range(min=0, max=2) ), vol.Optional( CONF_MAX_TOKENS, @@ -255,8 +267,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow): description={"suggested_value": DEFAULT_MAX_TOKENS}, ): vol.All( vol.Coerce(int), - vol.Range(min=1, max=4096), - msg="Max tokens must be between 1 and 4096" + vol.Range(min=1, max=4096) ), vol.Optional( CONF_REQUEST_INTERVAL, @@ -266,8 +277,7 @@ class OptionsFlowHandler(config_entries.OptionsFlow): description={"suggested_value": DEFAULT_REQUEST_INTERVAL}, ): vol.All( vol.Coerce(float), - vol.Range(min=0.1), - msg="Request interval must be at least 0.1 seconds" + vol.Range(min=0.1) ), }) diff --git a/custom_components/ha_text_ai/translations/ru.json b/custom_components/ha_text_ai/translations/ru.json index 84c8d86..4f4c9dc 100644 --- a/custom_components/ha_text_ai/translations/ru.json +++ b/custom_components/ha_text_ai/translations/ru.json @@ -28,6 +28,7 @@ "request_interval": { "name": "Интервал запросов", "description": "Минимальное время между API запросами в секундах. Увеличьте при превышении лимитов запросов." + } } } } @@ -42,7 +43,13 @@ "context_length": "Входные данные слишком длинные для выбранной модели. Уменьшите max_tokens или используйте модель с большим контекстом.", "api_error": "Ошибка API OpenAI. Проверьте логи для подробностей.", "timeout": "Превышено время ожидания ответа от API.", - "queue_full": "Очередь запросов переполнена. Попробуйте позже." + "queue_full": "Очередь запросов переполнена. Попробуйте позже.", + "invalid_url_format": "Неверный формат URL", + "invalid_input": "Неверные входные данные", + "invalid_temperature": "Температура должна быть от 0 до 2", + "invalid_max_tokens": "Максимальное количество токенов должно быть от 1 до 4096", + "invalid_request_interval": "Интервал запросов должен быть не менее 0.1 секунды", + "unknown": "Неизвестная ошибка" }, "abort": { "already_configured": "Эта интеграция OpenAI уже настроена", diff --git a/ha_text_ai.zip b/ha_text_ai.zip index 965c94c41ced0c070d14fda2a295d4c6d740fa02..d8a1452e6f46439e3811e4c8d5a6d7656bb7531f 100644 GIT binary patch delta 5415 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 delta 5305 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