Compare commits

..
Author SHA1 Message Date
SMKRV d29535245f Release v2.0.7-beta 2024-12-06 12:00:23 +03:00
SMKRV ca3ae982b0 feat(performance): Optimize system resources and token estimation
- Improve JSON history file processing
- Add memory and disk space validation
- Enhance parallel request handling
- Refine token counting heuristics
2024-12-06 03:14:52 +03:00
SMKRV 0c4399b46c feat(performance): Optimize system resources and token estimation
- Improve JSON history file processing
- Add memory and disk space validation
- Enhance parallel request handling
- Refine token counting heuristics
2024-12-06 02:53:41 +03:00
SMKRV c13ef1921d feat(performance): Optimize system resources and token estimation
- Improve JSON history file processing
- Add memory and disk space validation
- Enhance parallel request handling
- Refine token counting heuristics
2024-12-06 02:51:06 +03:00
SMKRV 0fb9acfa8f docs:(README) 2024-12-05 02:00:24 +03:00
SMKRV 2ac4389e1a docs:(README) 2024-12-05 01:55:31 +03:00
SMKRV de194e425c docs:(README) 2024-12-05 01:54:09 +03:00
SMKRV 95f3d6506e docs:(README) 2024-12-05 01:49:26 +03:00
SMKRV 3e877243b9 docs:(README) 2024-12-05 01:46:28 +03:00
SMKRV fd795c9e90 docs:(readme) 2024-12-05 00:36:08 +03:00
SMKRV 5f5dc041b9 docs: structure 2024-12-04 23:32:05 +03:00
SMKRV 3d3885d43d Update README.md 2024-12-04 23:28:14 +03:00
smkrvandGitHub b059744716 Update README.md 2024-12-04 23:23:46 +03:00
SMKRV f2a41aaa2c docs: logo 2024-12-04 20:13:08 +03:00
SMKRV 1fcf751d0d docs: logo 2024-12-04 19:24:30 +03:00
SMKRV a45c9407fd docs: logo 2024-12-04 19:19:40 +03:00
SMKRV 21fdf108cf docs: logo 2024-12-04 19:17:09 +03:00
SMKRV c7ec2b3ea4 docs: logo 2024-12-04 19:13:57 +03:00
SMKRV 6c4da6cea5 docs: logo 2024-12-04 19:12:44 +03:00
SMKRV 3029c5dd26 docs: logo 2024-12-04 19:12:21 +03:00
SMKRV 3e97028094 docs: logo 2024-12-04 19:11:54 +03:00
SMKRV 1194c87134 docs: logo 2024-12-04 17:51:40 +03:00
SMKRV e03b5315c2 logo update 2024-12-04 17:51:02 +03:00
SMKRV fad887492f docs: logo 2024-12-04 17:48:04 +03:00
SMKRV f26df29937 docs: logo 2024-12-04 17:47:12 +03:00
SMKRV 69b54a08ba logo update 2024-12-04 17:42:32 +03:00
SMKRV 7ce77eb18e docs: logo 2024-12-04 17:36:18 +03:00
SMKRV 1480669dc8 icons update 2024-12-04 17:34:19 +03:00
16 changed files with 813 additions and 432 deletions
+106 -72
View File
@@ -4,7 +4,7 @@
![GitHub release](https://img.shields.io/github/release/smkrv/ha-text-ai.svg?style=flat-square) ![GitHub last commit](https://img.shields.io/github/last-commit/smkrv/ha-text-ai.svg?style=flat-square) [![License: CC BY-NC-SA 4.0](https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg?style=flat-square)](https://creativecommons.org/licenses/by-nc-sa/4.0/) [![hacs_badge](https://img.shields.io/badge/HACS-Custom-41BDF5.svg?style=flat-square)](https://github.com/hacs/integration) ![/README.md](https://img.shields.io/badge/language-English-green?style=flat-square) ![/README_RU.md](https://img.shields.io/badge/language-Russian-green?style=flat-square) ![](https://img.shields.io/badge/language-Deutch-green?style=flat-square)
<img src="https://github.com/smkrv/ha-text-ai/blob/524849f6a945ec62c2cf6a6b7ecd9a28b37bf0fa/misc/icons/logo.jpg" alt="HA Text AI" height="160"/>
<img src="https://github.com/smkrv/ha-text-ai/blob/e03b5315c2cc7b489a6e1175da728075275ed614/misc/icons/logo.png" alt="HA Text AI" style="width: 80%; max-width: 640px; max-height: 150px; aspect-ratio: 2/1; object-fit: contain;"/>
### Advanced AI Integration for [Home Assistant](https://www.home-assistant.io/) with LLM multi-provider support
</div>
@@ -27,53 +27,67 @@ Transform your smart home experience with powerful AI assistance powered by mult
## 🌟 Features
- 🧠 **Multi-Provider AI Integration**:
- Support for OpenAI GPT models
- Anthropic Claude integration
- Custom API endpoints
- Flexible model selection
- 🧠 **Multi-Provider AI Integration**: Support for OpenAI GPT and Anthropic Claude 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
- 🎯 **Advanced Customization**: Per-request model and parameter selection
- 🔒 **Enhanced Security**: Secure API key management and usage monitoring
- 🎨 **Improved User Experience**: Intuitive configuration and rich interfaces
- 🔄 **Automation Integration**: Event-driven responses and template compatibility
- 💬 **Advanced Language Processing**:
- Context-aware responses
- Multi-turn conversations
- Custom system instructions
- Natural conversation flow
<details>
<summary>📦 Detailed Feature Breakdown</summary>
- 📝 **Enhanced Memory Management**:
- File-based conversation history storage
- Automatic history rotation
- Configurable history size limits
- Secure storage in Home Assistant
### 🧠 **Multi-Provider AI Integration**
- Support for OpenAI GPT models
- Anthropic Claude integration
- Custom API endpoints
- Flexible model selection
-**Performance Optimization**:
- Efficient token usage
- Smart rate limiting
- Response caching
- Request interval control
### 💬 **Advanced Language Processing**
- Context-aware responses
- Multi-turn conversations
- Custom system instructions
- Natural conversation flow
- 🎯 **Advanced Customization**:
- Per-request model selection
- Adjustable parameters
- Custom system prompts
- Temperature control
### 📝 **Enhanced Memory Management**
- File-based conversation history storage
- Automatic history rotation
- Configurable history size limits
- Secure storage in Home Assistant
- 🔒 **Enhanced Security**:
- Secure API key storage
- Rate limiting protection
- Error handling
- Usage monitoring
### ⚡ **Performance Optimization**
- Efficient token usage
- Smart rate limiting
- Response caching
- Request interval control
- 🎨 **Improved User Experience**:
- Intuitive configuration UI
- Detailed sensor attributes
- Rich service interface
- Model selection UI
### 🎯 **Advanced Customization**
- Per-request model selection
- Adjustable parameters
- Custom system prompts
- Temperature control
- 🔄 **Automation Integration**:
- Event-driven responses
- Conditional logic support
- Template compatibility
- Model-specific automation
### 🔒 **Enhanced Security**
- Secure API key storage
- Rate limiting protection
- Error handling
- Usage monitoring
### 🎨 **Improved User Experience**
- Intuitive configuration UI
- Detailed sensor attributes
- Rich service interface
- Model selection UI
### 🔄 **Automation Integration**
- Event-driven responses
- Conditional logic support
- Template compatibility
- Model-specific automation
</details>
## 📋 Prerequisites
@@ -85,37 +99,44 @@ Transform your smart home experience with powerful AI assistance powered by mult
- Python 3.9 or newer
- Stable internet connection
### Configuration Options
- API Provider (OpenAI/Anthropic)
- API Key (provider-specific)
- Model Selection (flexible, provider-specific models)
- Temperature (Creativity control, 0.0-2.0)
- Max Tokens (Response length limit)
- Request Interval (API call throttling)
- Custom API Endpoint (optional)
## Configuration Options
#### ⓘ Potentially Compatible Providers
The integration is designed to be flexible and may work with other providers offering OpenAI-compatible APIs:
- Groq
- Together AI
- Perplexity AI
- Mistral AI
- Google AI
- Local AI servers (like Ollama)
- Custom OpenAI-compatible endpoints
### 🔧 **Core Configuration Settings**
- 🌐 **API Provider**: OpenAI/Anthropic
- 🔑 **API Key**: Provider-specific authentication
- 🤖 **Model Selection**: Flexible, provider-specific models
- 🌡️ **Temperature**: Creativity control (0.0-2.0)
- 📏 **Max Tokens**: Response length limit (token usage is estimated using a heuristic method based on word count and specific word characteristics, which may differ from actual token usage)
- ⏱️ **Request Interval**: API call throttling
- 💾 **History Size**: Number of messages to retain
- 🌍 **Custom API Endpoint**: Optional advanced configuration
#### Additional Notes
- Not all providers guarantee full compatibility
- Performance may vary between providers
- Check individual provider's documentation
<details>
<summary>🌐 Potentially Compatible Providers</summary>
#### Flexible Provider Ecosystem
The integration is designed to be flexible and may work with other providers offering OpenAI-compatible APIs:
- Groq
- Together AI
- Perplexity AI
- Mistral AI
- Google AI
- Local AI servers (like Ollama)
- Custom OpenAI-compatible endpoints
#### 🚨 Compatibility Notes
- Not all providers guarantee full compatibility
- Performance may vary between providers
- Check individual provider's documentation
- Ensure your API key has sufficient credits/quota
#### Provider Compatibility Requirements
To be compatible, a provider should support:
- OpenAI-like REST API structure
- JSON request/response format
- Standard authentication method
#### 🔍 Provider Compatibility Requirements
To be compatible, a provider should support:
- OpenAI-like REST API structure
- JSON request/response format
- Standard authentication method
- Similar model parameter handling
</details>
## ⚡ Installation
@@ -144,7 +165,8 @@ To be compatible, a provider should support:
3. Search for "HA Text AI"
4. Follow the configuration steps
### Via YAML
<details>
<summary>📦 Via YAML (Advanced)</summary>
### Platform Configuration (Global Settings)
@@ -202,6 +224,8 @@ sensor:
| `temperature` | Float | ❌ | Platform setting | Override global temperature |
| `max_tokens` | Integer | ❌ | Platform setting | Override global max tokens |
</details>
## 🛠️ Available Services
### ask_question
@@ -258,7 +282,7 @@ sensor.ha_text_ai_YOUR_UNIQUE_SUFFIX
# Examples:
sensor.ha_text_ai_gpt # GPT-based sensor
sensor.ha_text_ai_claude # Claude-based sensor
sensor.ha_text_ai_gpt # Custom suffix
sensor.ha_text_ai_abc # Custom suffix
```
#### Response Retrieval
@@ -291,6 +315,16 @@ automation:
### 🔍 HA Text AI Sensor Attributes
- 🤖 **Model and Provider Information**: Tracking current AI model and service provider
- 🚦 **System Status**: Real-time API and processing readiness
- 📊 **Performance Metrics**: Request success rates and response times
- 💬 **Conversation Tracking**: Token usage and interaction history are estimated using a heuristic method based on word count and specific word characteristics, which may differ from actual token usage.
- 🕒 **Last Interaction Details**: Recent query and response tracking
- ❤️ **System Health**: Error monitoring and service uptime
<details>
<summary>📦 Detailed Sensor Attributes</summary>
#### Model and Provider Information
```yaml
# Name of the AI model currently in use (e.g., latest version of GPT)
@@ -352,7 +386,6 @@ automation:
# Last few conversation entries (limited to 3 for performance)
{{ state_attr('sensor.ha_text_ai_gpt', 'conversation_history') }} # [...]
```
#### Last Interaction Details
@@ -381,7 +414,7 @@ automation:
### History Storage
Conversation history stored in `.storage/ha_text_ai_history/` directory:
- Each instance has its own history file
- Each instance has its own history file (JSON)
- Files are automatically rotated when size limit is reached
- Archived history files are timestamped
- Default maximum file size: 1MB
@@ -391,6 +424,7 @@ Conversation history stored in `.storage/ha_text_ai_history/` directory:
- Use these attributes for monitoring and automation
- Some values might be 0 or empty initially
</details>
## 📘 FAQ
@@ -427,7 +461,6 @@ A: Yes, archived history files are stored with timestamps and can be accessed ma
**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.
## 🤝 Contributing
Contributions welcome! Please read our [Contributing Guide](CONTRIBUTING.md).
@@ -474,6 +507,7 @@ If you want to say thanks financially, you can send a small token of appreciatio
---
<div align="center"><img src="https://github.com/smkrv/ha-text-ai/blob/f2a41aaa2cd88adaf6929ba5f228205a315efdfa/custom_components/ha_text_ai/icons/dark_icon%402x.png" alt="HA Text AI" style="width: 128px; height: auto;"/></div>
<div align="center">
Made with ❤️ and Claude 3.5 Sonnet for the Home Assistant Community
+63 -13
View File
@@ -11,8 +11,9 @@ from __future__ import annotations
import logging
import os
import shutil
import hashlib
from datetime import datetime, timedelta
from typing import Any, Dict
from typing import Any, Dict, TypeVar
import voluptuous as vol
from async_timeout import timeout
@@ -52,11 +53,13 @@ from .const import (
SERVICE_SET_SYSTEM_PROMPT,
DEFAULT_MAX_HISTORY,
CONF_MAX_HISTORY_SIZE,
ICONS_SUBDOMAIN,
)
_LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
ConfigType = TypeVar("ConfigType", bound=Dict[str, Any])
SERVICE_SCHEMA_ASK_QUESTION = vol.Schema({
vol.Required("instance"): cv.string,
@@ -90,19 +93,18 @@ def get_coordinator_by_instance(hass: HomeAssistant, instance: str) -> HATextAIC
raise HomeAssistantError(f"Instance {instance} not found")
async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool:
"""Set up the HA Text AI component."""
hass.data.setdefault(DOMAIN, {})
def get_file_hash(file_path: str) -> str:
"""Calculate SHA256 hash of file."""
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
try:
source = os.path.join(os.path.dirname(__file__), 'icons', 'icon.svg')
dest_dir = os.path.join(hass.config.path('www'), 'icons')
os.makedirs(dest_dir, exist_ok=True)
dest = os.path.join(dest_dir, 'icon.png')
if not os.path.exists(dest):
shutil.copyfile(source, dest)
except Exception as ex:
_LOGGER.warning("Failed to copy custom icon: %s", str(ex))
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Home Assistant Text AI component."""
# Initialize domain data storage
hass.data.setdefault(DOMAIN, {})
async def async_ask_question(call: ServiceCall) -> None:
"""Handle ask_question service."""
@@ -150,6 +152,7 @@ async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool:
_LOGGER.error("Error setting system prompt: %s", str(err))
raise HomeAssistantError(f"Failed to set system prompt: {str(err)}")
# Register services
hass.services.async_register(
DOMAIN,
SERVICE_ASK_QUESTION,
@@ -178,6 +181,53 @@ async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool:
schema=SERVICE_SCHEMA_SET_SYSTEM_PROMPT
)
# Handle icons
try:
source_icon_path = os.path.join(
os.path.dirname(__file__),
ICONS_SUBDOMAIN,
'icon@2x.png'
)
destination_directory = os.path.join(
hass.config.path('www'),
DOMAIN,
ICONS_SUBDOMAIN
)
destination_icon_path = os.path.join(
destination_directory,
'icon.png'
)
if not os.path.exists(source_icon_path):
_LOGGER.error("Source icon not found: %s", source_icon_path)
return True
def create_directory():
os.makedirs(destination_directory, exist_ok=True)
await hass.async_add_executor_job(create_directory)
should_copy = True
if os.path.exists(destination_icon_path):
source_hash = await hass.async_add_executor_job(get_file_hash, source_icon_path)
dest_hash = await hass.async_add_executor_job(get_file_hash, destination_icon_path)
should_copy = source_hash != dest_hash
if should_copy:
def copy_file():
shutil.copyfile(source_icon_path, destination_icon_path)
await hass.async_add_executor_job(copy_file)
_LOGGER.debug("Icon updated: %s", destination_icon_path)
except PermissionError as e:
_LOGGER.error("Permission denied when managing icons: %s", str(e))
except Exception as e:
_LOGGER.error("Failed to manage icons: %s", str(e))
return True
async def async_check_api(session, endpoint: str, headers: dict, provider: str) -> bool:
+3 -1
View File
@@ -41,7 +41,9 @@ CONF_IS_ANTHROPIC: Final = "is_anthropic"
CONF_CONTEXT_MESSAGES: Final = "context_messages"
ABSOLUTE_MAX_HISTORY_SIZE = 500
MAX_ENTRY_SIZE = 1 * 1024 * 1024
MAX_ATTRIBUTE_SIZE = 4 * 1024
MAX_HISTORY_FILE_SIZE = 1 * 1024 * 1024
ICONS_SUBDOMAIN = "icons"
# Default values
DEFAULT_MODEL: Final = "gpt-4o-mini"
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 259 KiB

+8 -8
View File
@@ -13,16 +13,16 @@
"loggers": ["custom_components.ha_text_ai"],
"mqtt": [],
"quality_scale": "silver",
"requirements": [
"openai>=1.12.0",
"anthropic>=0.8.0",
"aiohttp>=3.8.0",
"async-timeout>=4.0.0",
"certifi>=2024.2.2"
],
"requirements": [
"openai>=1.12.0",
"anthropic>=0.8.0",
"aiohttp>=3.8.0",
"async-timeout>=4.0.0",
"certifi>=2024.2.2"
],
"single_config_entry": false,
"ssdp": [],
"usb": [],
"version": "2.0.5-beta",
"version": "2.0.7-beta",
"zeroconf": []
}
+68 -47
View File
@@ -67,6 +67,7 @@ from .const import (
ENTITY_ICON_PROCESSING,
DEFAULT_NAME_PREFIX,
CONF_MAX_HISTORY_SIZE,
MAX_ATTRIBUTE_SIZE,
)
from .coordinator import HATextAICoordinator
@@ -178,12 +179,29 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
def _sanitize_attributes(self, attributes: Dict[str, Any]) -> Dict[str, Any]:
"""Sanitize all attributes for JSON serialization."""
return {
sanitized = {
key: self._sanitize_value(value)
for key, value in attributes.items()
if value is not None
}
# Log metrics for debugging
metrics_keys = [
METRIC_TOTAL_TOKENS,
METRIC_PROMPT_TOKENS,
METRIC_COMPLETION_TOKENS,
METRIC_SUCCESSFUL_REQUESTS,
METRIC_FAILED_REQUESTS,
METRIC_AVERAGE_LATENCY,
METRIC_MAX_LATENCY,
METRIC_MIN_LATENCY,
]
metrics_values = {k: sanitized.get(k) for k in metrics_keys if k in sanitized}
_LOGGER.debug(f"Metrics for {self.entity_id}: {metrics_values}")
return sanitized
@property
def native_value(self) -> StateType:
"""Return the native value of the sensor."""
@@ -212,68 +230,65 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
try:
data = self.coordinator.data
metrics = data.get("metrics", {})
# Base attributes
attributes = {
ATTR_MODEL: self._config_entry.data.get(CONF_MODEL, "Unknown"),
ATTR_API_PROVIDER: self._config_entry.data.get(
CONF_API_PROVIDER, "Unknown"
),
ATTR_API_PROVIDER: self._config_entry.data.get(CONF_API_PROVIDER, "Unknown"),
ATTR_API_STATUS: self._current_state,
ATTR_TOTAL_ERRORS: self._error_count,
ATTR_LAST_ERROR: self._last_error,
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"),
ATTR_SYSTEM_PROMPT: (data.get("system_prompt", "")[:MAX_ATTRIBUTE_SIZE]
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),
ATTR_IS_MAINTENANCE: data.get("is_maintenance", False),
ATTR_ENDPOINT_STATUS: data.get("endpoint_status", "unknown"),
ATTR_UPTIME: data.get("uptime", 0),
ATTR_UPTIME: round(data.get("uptime", 0), 2),
ATTR_HISTORY_SIZE: data.get("history_size", 0),
ATTR_CONVERSATION_HISTORY: data.get("conversation_history", []),
}
# Add metrics
metrics = data.get("metrics", {})
if isinstance(metrics, dict):
self._metrics = metrics
attributes.update(
{
METRIC_TOTAL_TOKENS: metrics.get("total_tokens", 0),
METRIC_PROMPT_TOKENS: metrics.get("prompt_tokens", 0),
METRIC_COMPLETION_TOKENS: metrics.get("completion_tokens", 0),
METRIC_SUCCESSFUL_REQUESTS: metrics.get(
"successful_requests", 0
),
METRIC_FAILED_REQUESTS: metrics.get("failed_requests", 0),
METRIC_AVERAGE_LATENCY: metrics.get("average_latency", 0),
METRIC_MAX_LATENCY: metrics.get("max_latency", 0),
METRIC_MIN_LATENCY: metrics.get("min_latency", float("inf")),
# History limit
conversation_history = data.get("conversation_history", [])
if conversation_history:
limited_history = []
for entry in conversation_history:
limited_entry = {
"timestamp": entry["timestamp"],
"question": entry["question"][:MAX_ATTRIBUTE_SIZE],
"response": entry["response"][:MAX_ATTRIBUTE_SIZE]
}
)
limited_history.append(limited_entry)
attributes[ATTR_CONVERSATION_HISTORY] = limited_history
# Add last response
# Metrics
if isinstance(metrics, dict):
attributes.update({
METRIC_TOTAL_TOKENS: metrics.get("total_tokens", 0),
METRIC_PROMPT_TOKENS: metrics.get("prompt_tokens", 0),
METRIC_COMPLETION_TOKENS: metrics.get("completion_tokens", 0),
METRIC_SUCCESSFUL_REQUESTS: metrics.get("successful_requests", 0),
METRIC_FAILED_REQUESTS: metrics.get("failed_requests", 0),
METRIC_AVERAGE_LATENCY: round(metrics.get("average_latency", 0), 2),
METRIC_MAX_LATENCY: round(metrics.get("max_latency", 0), 2),
METRIC_MIN_LATENCY: (metrics.get("min_latency")
if metrics.get("min_latency") != float("inf")
else None),
})
# Last response handling
last_response = data.get("last_response", {})
if isinstance(last_response, dict):
self._last_response = last_response
attributes.update(
{
ATTR_RESPONSE: last_response.get("response", ""),
ATTR_QUESTION: last_response.get("question", ""),
"last_model": last_response.get("model", ""),
"last_timestamp": last_response.get("timestamp", ""),
"last_error": last_response.get("error"),
}
)
# Add performance metrics if available
if ATTR_PERFORMANCE_METRICS in data:
attributes[ATTR_PERFORMANCE_METRICS] = data[
ATTR_PERFORMANCE_METRICS
]
# Add API version if available
if ATTR_API_VERSION in data:
attributes[ATTR_API_VERSION] = data[ATTR_API_VERSION]
attributes.update({
ATTR_RESPONSE: last_response.get("response", "")[:MAX_ATTRIBUTE_SIZE],
ATTR_QUESTION: last_response.get("question", "")[:MAX_ATTRIBUTE_SIZE],
"last_model": last_response.get("model", ""),
"last_timestamp": last_response.get("timestamp", ""),
"last_error": (last_response.get("error", "")[:MAX_ATTRIBUTE_SIZE]
if last_response.get("error") else None),
})
return self._sanitize_attributes(attributes)
@@ -299,6 +314,12 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
self._is_processing = data.get("is_processing", False)
# Update metrics
metrics = data.get("metrics", {})
if isinstance(metrics, dict):
self._metrics.update(metrics)
_LOGGER.debug(f"Updated metrics for {self.entity_id}: {self._metrics}")
# Update conversation history and system prompt
self._conversation_history = data.get("conversation_history", [])
self._system_prompt = data.get("system_prompt")
Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

+29 -22
View File
@@ -1,25 +1,32 @@
```
ha-text-ai/
├── custom_components/
├── ha_text_ai/
├── __init__.py
├── config_flow.py
├── coordinator.py
├── manifest.json
├── sensor.py
├── services.yaml
├── const.py
└── api_client.py
├── translations/
│ ├── en.json
│ ├── de.json
└── ru.json
── icons/
├── icon.png
├── icon@2x.png
├── logo.png
── logo@2x.png
├── custom_components
│   └── ha_text_ai
│   ├── __init__.py
│   ├── api_client.py
│   ├── config_flow.py
│   ├── const.py
│   ├── coordinator.py
│   ├── icons
│      ├── dark_icon.png
│      ├── dark_icon@2x.png
│      ├── dark_logo.png
│      ├── dark_logo@2x.png
│   │   ├── icon.png
│   │   ├── icon@2x.png
│   │   ├── logo.png
│      └── logo@2x.png
│   ├── manifest.json
│   ── sensor.py
│   ├── services.yaml
│   └── translations
│   ├── de.json
│   ── en.json
│   ├── es.json
│   ├── hi.json
│   ├── it.json
│   ├── ru.json
│   ├── sr.json
│   └── zh.json
```