Compare commits

..
44 Commits
Author SHA1 Message Date
SMKRV 2d68f29ab5 bump to version 2.0.8 2024-12-09 00:41:28 +03:00
SMKRV 19d4a93bca bump to version 2.0.8 2024-12-09 00:41:08 +03:00
SMKRV fb75c5f44e bump to version 2.0.9 2024-12-09 00:39:28 +03:00
SMKRV 52af987985 bump to version 2.0.9 2024-12-09 00:38:59 +03:00
SMKRV 162a30acdd bump to version 2.0.8-beta 2024-12-07 00:48:17 +03:00
SMKRV 898a6fc638 bump to version 2.0.8-beta 2024-12-07 00:43:53 +03:00
SMKRV 38e2362be4 bump to version 2.0.8-beta 2024-12-06 16:51:32 +03:00
SMKRV cea912d0b5 bump to version 2.0.8-beta 2024-12-06 16:51:09 +03:00
SMKRV 5ec00040c2 bump to version 2.0.8-beta 2024-12-06 16:50:26 +03:00
SMKRV 8d28fc4a0d bump to version 2.0.8-beta 2024-12-06 16:46:11 +03:00
SMKRV 7cc6587724 bump to version 2.0.8-beta 2024-12-06 16:35:26 +03:00
SMKRV bacf76e0a9 bump to version 2.0.8-beta 2024-12-06 16:33:46 +03:00
SMKRV f86c7bfd57 refactor(docs): relocate README images from misc/ to assets/ 2024-12-06 16:13:27 +03:00
SMKRV 2aaf340575 refactor(docs): relocate README images from misc/ to assets/ 2024-12-06 16:11:13 +03:00
SMKRV bae11ba85c refactor(docs): relocate README images from misc/ to assets/ 2024-12-06 16:10:04 +03:00
SMKRV 9eb7d8912c fix: sensor history attribute calculation 2024-12-06 12:26:56 +03:00
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
24 changed files with 863 additions and 444 deletions
+127 -76
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/2aaf3405759eb2d97624834594e24ace896131df/assets/images/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>
@@ -17,63 +17,88 @@ Transform your smart home experience with powerful AI assistance powered by mult
---
> [!IMPORTANT]
> 🚧 BETA VERSION
> Expect: potential bugs, frequent changes, incomplete features.
> 🤝 Community Driven
>
> <a href="https://community.home-assistant.io/t/ha-text-ai-transforming-home-automation-with-multi-provider-language-models/799741"><img src="https://img.shields.io/badge/Community-blue?style=for-the-badge&logo=homeassistant&logoColor=white&color=03a9f4"/></a>
>
> [Screenshots](misc/screenshots/screenshot.jpg)
> [Screenshots](assets/images/screenshots/screenshot.jpg)
## 🌟 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>
#### 🌐 Translations
| Code | Language | Status |
|------|----------|--------|
| 🇩🇪 de | Deutsch | Full |
| 🇬🇧 en | English | Primary |
| 🇪🇸 es | Español | Full |
| 🇮🇳 hi | हिन्दी | Full |
| 🇮🇹 it | Italiano | Full |
| 🇷🇺 ru | Русский | Full |
| 🇷🇸 sr | Српски | Full |
| 🇨🇳 zh | 中文 | Full |
## 📋 Prerequisites
@@ -82,41 +107,50 @@ Transform your smart home experience with powerful AI assistance powered by mult
- OpenAI ([Get key](https://platform.openai.com/account/api-keys))
- Anthropic ([Get key](https://console.anthropic.com/))
- OpenRouter ([Get key](https://openrouter.ai/keys))
- Any OpenAI-compatible API provider
- 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
### HACS Installation (Recommended)
@@ -144,7 +178,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 +237,8 @@ sensor:
| `temperature` | Float | ❌ | Platform setting | Override global temperature |
| `max_tokens` | Integer | ❌ | Platform setting | Override global max tokens |
</details>
## 🛠️ Available Services
### ask_question
@@ -214,12 +251,14 @@ data:
max_tokens: 500 # optional
context_messages: 10 #optional, number of previous messages to include in context, default: 5
system_prompt: "You are a sleep optimization expert" # optional
instance: sensor.ha_text_ai_gpt
```
### set_system_prompt
```yaml
service: ha_text_ai.set_system_prompt
data:
instance: sensor.ha_text_ai_gpt
prompt: |
You are a home automation expert focused on:
1. Energy efficiency
@@ -258,7 +297,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
@@ -275,6 +314,7 @@ automation:
- service: ha_text_ai.ask_question
data:
question: "Home automation advice"
instance: sensor.ha_text_ai_gpt
- service: notify.mobile
data:
message: >
@@ -291,6 +331,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 +402,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 +430,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 +440,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 +477,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,9 +523,11 @@ 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/2aaf3405759eb2d97624834594e24ace896131df/assets/images/icons/footer_icon.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
Made with ❤️ for the Home Assistant Community,
utilizing Claude 3.5 Sonnet, Gemini Pro 1.5, and Qwen 2.5 Coder 32B Instruct.
[Report Bug](https://github.com/smkrv/ha-text-ai/issues) · [Request Feature](https://github.com/smkrv/ha-text-ai/issues)
Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Before

Width:  |  Height:  |  Size: 618 KiB

After

Width:  |  Height:  |  Size: 618 KiB

Before

Width:  |  Height:  |  Size: 923 KiB

After

Width:  |  Height:  |  Size: 923 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

+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.8",
"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")
@@ -95,7 +95,7 @@
},
"services": {
"ask_question": {
"name": "Hacer Pregunta (IA de Texto de HA)",
"name": "Hacer Pregunta (HA Text AI)",
"description": "Envía una pregunta al modelo de IA y recibe una respuesta detallada. La respuesta se almacenará en el historial de conversación y se podrá recuperar más tarde.",
"fields": {
"instance": {
@@ -95,7 +95,7 @@
},
"services": {
"ask_question": {
"name": "प्रश्न पूछें (एचए टेक्स्ट एआई)",
"name": "प्रश्न पूछें (HA Text AI)",
"description": "एआई मॉडल को एक प्रश्न भेजें और विस्तृत प्रतिक्रिया प्राप्त करें। प्रतिक्रिया बातचीत के इतिहास में संग्रहीत की जाएगी और बाद में पुनर्प्राप्त की जा सकती है।",
"fields": {
"instance": {
@@ -95,7 +95,7 @@
},
"services": {
"ask_question": {
"name": "Задать вопрос (Текстовый ИИ HA)",
"name": "Задать вопрос (HA Text AI)",
"description": "Отправить вопрос модели ИИ и получить подробный ответ. Ответ будет сохранен в истории разговора и может быть получен позже.",
"fields": {
"instance": {
@@ -95,7 +95,7 @@
},
"services": {
"ask_question": {
"name": "提问HA文本AI",
"name": "提问 (HA Text AI)",
"description": "向AI模型发送问题并接收详细响应。响应将存储在对话历史中,可以稍后检索。",
"fields": {
"instance": {
+28 -23
View File
@@ -1,25 +1,30 @@
```
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
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
```