Compare commits

..
19 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
13 changed files with 112 additions and 76 deletions
+27 -10
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/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;"/>
<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,13 +17,11 @@ 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
@@ -34,7 +32,7 @@ Transform your smart home experience with powerful AI assistance powered by mult
- 🎯 **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
- 🔄 **Automation Integration**: Event-driven responses and template compatibility
<details>
<summary>📦 Detailed Feature Breakdown</summary>
@@ -89,6 +87,19 @@ Transform your smart home experience with powerful AI assistance powered by mult
</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
- Home Assistant 2024.11 or later
@@ -96,6 +107,7 @@ 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
@@ -106,7 +118,7 @@ Transform your smart home experience with powerful AI assistance powered by mult
- 🔑 **API Key**: Provider-specific authentication
- 🤖 **Model Selection**: Flexible, provider-specific models
- 🌡️ **Temperature**: Creativity control (0.0-2.0)
- 📏 **Max Tokens**: Response length limit
- 📏 **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
@@ -136,6 +148,7 @@ To be compatible, a provider should support:
- JSON request/response format
- Standard authentication method
- Similar model parameter handling
</details>
## ⚡ Installation
@@ -238,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
@@ -299,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: >
@@ -318,7 +334,7 @@ automation:
- 🤖 **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 (for more precise token counting, install `tiktoken`; otherwise, a fallback estimation method is automatically used)
- 💬 **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
@@ -414,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
@@ -507,10 +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/f2a41aaa2cd88adaf6929ba5f228205a315efdfa/custom_components/ha_text_ai/icons/dark_icon%402x.png" alt="HA Text AI" style="width: 128px; height: auto;"/></div>
<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

Before

Width:  |  Height:  |  Size: 339 KiB

After

Width:  |  Height:  |  Size: 339 KiB

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

+53 -32
View File
@@ -173,7 +173,6 @@ class HATextAICoordinator(DataUpdateCoordinator):
self.available = True
self._state = STATE_READY
self._conversation_history = [] # Full conversation history
self._start_time = dt_util.utcnow()
@@ -182,13 +181,13 @@ class HATextAICoordinator(DataUpdateCoordinator):
hass.config.path(".storage"),
"ha_text_ai_history"
)
hass.async_create_task(self._create_history_dir())
hass.async_create_task(self._check_history_directory())
hass.async_create_task(self._migrate_history_from_txt_to_json())
hass.async_create_task(self._initialize_metrics())
# Initialize all async tasks in proper order
self.hass.async_create_task(self._create_history_dir())
self.hass.async_create_task(self._check_history_directory())
self.hass.async_create_task(self._initialize_metrics())
self.hass.async_create_task(self.async_initialize_history_file())
self.hass.async_create_task(self._migrate_history_from_txt_to_json())
# History file path using instance name
self._history_file = os.path.join(
@@ -273,18 +272,17 @@ class HATextAICoordinator(DataUpdateCoordinator):
_LOGGER.error(f"Error checking file existence for {path}: {e}")
return False
async def _create_history_dir(self):
async def _create_history_dir(self) -> None:
"""
Asynchronously create history directory.
Creates the directory for storing history files
without blocking the event loop.
"""
try:
await self.hass.async_add_executor_job(
lambda: os.makedirs(self._history_dir, exist_ok=True)
)
_LOGGER.debug(f"Directory creation details: exist_ok=True")
def mkdir_sync():
os.makedirs(self._history_dir, exist_ok=True)
await self.hass.async_add_executor_job(mkdir_sync)
_LOGGER.debug(f"History directory created/verified: {self._history_dir}")
except PermissionError:
_LOGGER.error(f"Permission denied when creating history directory: {self._history_dir}")
raise
@@ -405,27 +403,38 @@ class HATextAICoordinator(DataUpdateCoordinator):
async def async_initialize_history_file(self) -> None:
"""
Asynchronously initialize history file.
Creates the file and writes an initialization timestamp
without blocking the event loop using Home Assistant's executor.
Asynchronously initialize history file and load existing history.
"""
try:
await self.hass.async_add_executor_job(self._sync_initialize_history_file)
await self._create_history_dir()
if await self._file_exists(self._history_file):
# Load existing history
async with AsyncFileHandler(self._history_file, 'r') as f:
content = await f.read()
if content:
history = json.loads(content)
# Validate and load history
if isinstance(history, list):
self._conversation_history = history[-self.max_history_size:]
_LOGGER.debug(
f"Loaded {len(self._conversation_history)} history entries "
f"for {self.instance_name}"
)
else:
# Create new history file
async with AsyncFileHandler(self._history_file, 'w') as f:
await f.write(json.dumps([]))
await self._check_history_size()
await self.async_update_ha_state()
_LOGGER.debug(f"History file initialized: {self._history_file}")
except Exception as e:
_LOGGER.error(f"Could not initialize history file: {e}")
_LOGGER.debug(traceback.format_exc())
async def _sync_initialize_history_file(self) -> None:
try:
if not await self._file_exists(self._history_file):
def write_empty_history():
with open(self._history_file, 'w') as f:
json.dump([], f)
await self.hass.async_add_executor_job(write_empty_history)
except Exception as e:
_LOGGER.error(f"History file initialization failed: {e}")
# Size check to _update_history method
async def _update_history(self, question: str, response: dict) -> None:
"""Update conversation history with size validation."""
@@ -509,6 +518,15 @@ class HATextAICoordinator(DataUpdateCoordinator):
_LOGGER.error(f"Error writing history entry: {e}")
_LOGGER.debug(traceback.format_exc())
async def _check_history_size(self) -> None:
"""Verify and adjust history size if needed."""
if len(self._conversation_history) > self.max_history_size:
_LOGGER.warning(
f"History size ({len(self._conversation_history)}) exceeds "
f"maximum ({self.max_history_size}). Trimming..."
)
self._conversation_history = self._conversation_history[-self.max_history_size:]
async def _check_file_size(self, file_path: str) -> int:
"""
Check file size asynchronously.
@@ -565,7 +583,10 @@ class HATextAICoordinator(DataUpdateCoordinator):
Asynchronously check history directory permissions and writability.
"""
try:
# Test write permission in a separate thread
# First ensure directory exists
await self._create_history_dir()
# Then test write permission in a separate thread
test_file_path = os.path.join(self._history_dir, ".write_test")
await self.hass.async_add_executor_job(self._sync_test_directory_write, test_file_path)
@@ -754,7 +775,7 @@ class HATextAICoordinator(DataUpdateCoordinator):
"normalized_name": self.normalized_name,
}
def _calculate_context_tokens(self, messages: List[Dict[str, str]]) -> int:
def _calculate_context_tokens(self, messages: List[Dict[str, str]], model: Optional[str] = None) -> int:
total_tokens = 0
# Compile regular expressions for performance
+1 -1
View File
@@ -23,6 +23,6 @@
"single_config_entry": false,
"ssdp": [],
"usb": [],
"version": "2.0.6-beta",
"version": "2.0.8",
"zeroconf": []
}
@@ -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": {
+27 -29
View File
@@ -1,32 +1,30 @@
```
ha-text-ai/
├── 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
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
```