mirror of
https://github.com/smkrv/ha-text-ai.git
synced 2026-07-23 07:33:58 +08:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d29535245f | ||
|
|
ca3ae982b0 | ||
|
|
0c4399b46c |
@@ -106,7 +106,7 @@ Transform your smart home experience with powerful AI assistance powered by mult
|
|||||||
- 🔑 **API Key**: Provider-specific authentication
|
- 🔑 **API Key**: Provider-specific authentication
|
||||||
- 🤖 **Model Selection**: Flexible, provider-specific models
|
- 🤖 **Model Selection**: Flexible, provider-specific models
|
||||||
- 🌡️ **Temperature**: Creativity control (0.0-2.0)
|
- 🌡️ **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
|
- ⏱️ **Request Interval**: API call throttling
|
||||||
- 💾 **History Size**: Number of messages to retain
|
- 💾 **History Size**: Number of messages to retain
|
||||||
- 🌍 **Custom API Endpoint**: Optional advanced configuration
|
- 🌍 **Custom API Endpoint**: Optional advanced configuration
|
||||||
@@ -318,7 +318,7 @@ automation:
|
|||||||
- 🤖 **Model and Provider Information**: Tracking current AI model and service provider
|
- 🤖 **Model and Provider Information**: Tracking current AI model and service provider
|
||||||
- 🚦 **System Status**: Real-time API and processing readiness
|
- 🚦 **System Status**: Real-time API and processing readiness
|
||||||
- 📊 **Performance Metrics**: Request success rates and response times
|
- 📊 **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
|
- 🕒 **Last Interaction Details**: Recent query and response tracking
|
||||||
- ❤️ **System Health**: Error monitoring and service uptime
|
- ❤️ **System Health**: Error monitoring and service uptime
|
||||||
|
|
||||||
@@ -414,7 +414,7 @@ automation:
|
|||||||
|
|
||||||
### History Storage
|
### History Storage
|
||||||
Conversation history stored in `.storage/ha_text_ai_history/` directory:
|
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
|
- Files are automatically rotated when size limit is reached
|
||||||
- Archived history files are timestamped
|
- Archived history files are timestamped
|
||||||
- Default maximum file size: 1MB
|
- Default maximum file size: 1MB
|
||||||
|
|||||||
@@ -182,13 +182,13 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
|||||||
hass.config.path(".storage"),
|
hass.config.path(".storage"),
|
||||||
"ha_text_ai_history"
|
"ha_text_ai_history"
|
||||||
)
|
)
|
||||||
hass.async_create_task(self._create_history_dir())
|
|
||||||
|
|
||||||
hass.async_create_task(self._check_history_directory())
|
# Initialize all async tasks in proper order
|
||||||
|
self.hass.async_create_task(self._create_history_dir())
|
||||||
hass.async_create_task(self._migrate_history_from_txt_to_json())
|
self.hass.async_create_task(self._check_history_directory())
|
||||||
|
self.hass.async_create_task(self._initialize_metrics())
|
||||||
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
|
# History file path using instance name
|
||||||
self._history_file = os.path.join(
|
self._history_file = os.path.join(
|
||||||
@@ -273,18 +273,17 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
|||||||
_LOGGER.error(f"Error checking file existence for {path}: {e}")
|
_LOGGER.error(f"Error checking file existence for {path}: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def _create_history_dir(self):
|
async def _create_history_dir(self) -> None:
|
||||||
"""
|
"""
|
||||||
Asynchronously create history directory.
|
Asynchronously create history directory.
|
||||||
|
|
||||||
Creates the directory for storing history files
|
|
||||||
without blocking the event loop.
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
await self.hass.async_add_executor_job(
|
def mkdir_sync():
|
||||||
lambda: os.makedirs(self._history_dir, exist_ok=True)
|
os.makedirs(self._history_dir, exist_ok=True)
|
||||||
)
|
|
||||||
_LOGGER.debug(f"Directory creation details: exist_ok=True")
|
await self.hass.async_add_executor_job(mkdir_sync)
|
||||||
|
_LOGGER.debug(f"History directory created/verified: {self._history_dir}")
|
||||||
|
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
_LOGGER.error(f"Permission denied when creating history directory: {self._history_dir}")
|
_LOGGER.error(f"Permission denied when creating history directory: {self._history_dir}")
|
||||||
raise
|
raise
|
||||||
@@ -406,26 +405,22 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
|||||||
async def async_initialize_history_file(self) -> None:
|
async def async_initialize_history_file(self) -> None:
|
||||||
"""
|
"""
|
||||||
Asynchronously initialize history file.
|
Asynchronously initialize history file.
|
||||||
|
|
||||||
Creates the file and writes an initialization timestamp
|
|
||||||
without blocking the event loop using Home Assistant's executor.
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
await self.hass.async_add_executor_job(self._sync_initialize_history_file)
|
# Ensure directory exists first
|
||||||
|
await self._create_history_dir()
|
||||||
|
|
||||||
|
# Initialize file
|
||||||
|
if not await self._file_exists(self._history_file):
|
||||||
|
async with AsyncFileHandler(self._history_file, 'w') as f:
|
||||||
|
await f.write(json.dumps([]))
|
||||||
|
|
||||||
|
_LOGGER.debug(f"History file initialized: {self._history_file}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_LOGGER.error(f"Could not initialize history file: {e}")
|
_LOGGER.error(f"Could not initialize history file: {e}")
|
||||||
_LOGGER.debug(traceback.format_exc())
|
_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
|
# Size check to _update_history method
|
||||||
async def _update_history(self, question: str, response: dict) -> None:
|
async def _update_history(self, question: str, response: dict) -> None:
|
||||||
"""Update conversation history with size validation."""
|
"""Update conversation history with size validation."""
|
||||||
@@ -565,7 +560,10 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
|||||||
Asynchronously check history directory permissions and writability.
|
Asynchronously check history directory permissions and writability.
|
||||||
"""
|
"""
|
||||||
try:
|
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")
|
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)
|
await self.hass.async_add_executor_job(self._sync_test_directory_write, test_file_path)
|
||||||
|
|
||||||
@@ -754,7 +752,7 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
|||||||
"normalized_name": self.normalized_name,
|
"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
|
total_tokens = 0
|
||||||
|
|
||||||
# Compile regular expressions for performance
|
# Compile regular expressions for performance
|
||||||
|
|||||||
@@ -23,6 +23,6 @@
|
|||||||
"single_config_entry": false,
|
"single_config_entry": false,
|
||||||
"ssdp": [],
|
"ssdp": [],
|
||||||
"usb": [],
|
"usb": [],
|
||||||
"version": "2.0.6-beta",
|
"version": "2.0.7-beta",
|
||||||
"zeroconf": []
|
"zeroconf": []
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user