diff --git a/custom_components/ha_text_ai/history.py b/custom_components/ha_text_ai/history.py index b347f7d..6480a0e 100644 --- a/custom_components/ha_text_ai/history.py +++ b/custom_components/ha_text_ai/history.py @@ -123,6 +123,7 @@ class HistoryManager: @staticmethod def _sync_test_directory_write(test_file_path: str) -> None: try: + os.makedirs(os.path.dirname(test_file_path), mode=0o755, exist_ok=True) with open(test_file_path, "w") as f: f.write("Permission test") os.remove(test_file_path) diff --git a/custom_components/ha_text_ai/sensor.py b/custom_components/ha_text_ai/sensor.py index 7ad311c..989a105 100644 --- a/custom_components/ha_text_ai/sensor.py +++ b/custom_components/ha_text_ai/sensor.py @@ -46,7 +46,6 @@ from .const import ( ATTR_SYSTEM_PROMPT, ATTR_RESPONSE, ATTR_QUESTION, - ATTR_CONVERSATION_HISTORY, METRIC_TOTAL_TOKENS, METRIC_PROMPT_TOKENS, METRIC_COMPLETION_TOKENS, @@ -67,7 +66,6 @@ from .const import ( ENTITY_ICON_PROCESSING, DEFAULT_NAME_PREFIX, CONF_MAX_HISTORY_SIZE, - MAX_ATTRIBUTE_SIZE, VERSION, ) @@ -76,6 +74,11 @@ from .utils import safe_log_data _LOGGER = logging.getLogger(__name__) +# HA Recorder limit is 16384 bytes for state_attributes. +# Budget per field to stay well within the limit. +_ATTR_TEXT_LIMIT = 2048 +_ATTR_PROMPT_LIMIT = 512 + async def async_setup_entry( hass: HomeAssistant, @@ -243,7 +246,7 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): ATTR_TOTAL_ERRORS: metrics.get("total_errors", 0), "instance_name": self._instance_name, "normalized_name": self._normalized_name, - ATTR_SYSTEM_PROMPT: (data.get("system_prompt", "")[:MAX_ATTRIBUTE_SIZE] + ATTR_SYSTEM_PROMPT: (data.get("system_prompt", "")[:_ATTR_PROMPT_LIMIT] if data.get("system_prompt") else None), ATTR_IS_PROCESSING: data.get("is_processing", False), ATTR_IS_RATE_LIMITED: data.get("is_rate_limited", False), @@ -253,18 +256,19 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): ATTR_HISTORY_SIZE: data.get("history_size", 0), } - # History limit + # Conversation history preview (compact: last 3, truncated to 256 chars). + # Full history is available via ha_text_ai.get_history service. conversation_history = data.get("conversation_history", []) if conversation_history: - limited_history = [] - for entry in conversation_history: - limited_entry = { + preview = conversation_history[-3:] + attributes["conversation_history"] = [ + { "timestamp": entry["timestamp"], - "question": entry["question"][:MAX_ATTRIBUTE_SIZE], - "response": entry["response"][:MAX_ATTRIBUTE_SIZE] + "question": entry["question"][:256], + "response": entry["response"][:256], } - limited_history.append(limited_entry) - attributes[ATTR_CONVERSATION_HISTORY] = limited_history + for entry in preview + ] # Metrics if isinstance(metrics, dict): @@ -283,11 +287,11 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): last_response = data.get("last_response", {}) if isinstance(last_response, dict): attributes.update({ - ATTR_RESPONSE: last_response.get("response", "")[:MAX_ATTRIBUTE_SIZE], - ATTR_QUESTION: last_response.get("question", "")[:MAX_ATTRIBUTE_SIZE], + ATTR_RESPONSE: last_response.get("response", "")[:_ATTR_TEXT_LIMIT], + ATTR_QUESTION: last_response.get("question", "")[:_ATTR_TEXT_LIMIT], "last_model": last_response.get("model", ""), "last_timestamp": last_response.get("timestamp", ""), - "last_error": (last_response.get("error", "")[:MAX_ATTRIBUTE_SIZE] + "last_error": (last_response.get("error", "")[:_ATTR_TEXT_LIMIT] if last_response.get("error") else None), })