mirror of
https://github.com/smkrv/ha-text-ai.git
synced 2026-07-22 15:14:01 +08:00
Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d064f5b9d | ||
|
|
ae8f8bda03 | ||
|
|
2dbae19c41 | ||
|
|
d3ef31f551 | ||
|
|
ff0c0369e8 | ||
|
|
b0dafe081b | ||
|
|
a88b5d01c1 | ||
|
|
e7d5e62671 | ||
|
|
5f41b9489d | ||
|
|
958e241e0e | ||
|
|
4b3efc0b6f | ||
|
|
1592fc2371 | ||
|
|
d5a6613428 | ||
|
|
9324473c9e | ||
|
|
456e797cca | ||
|
|
f960b9f9b4 | ||
|
|
8449b73423 | ||
|
|
f19ba9aac9 | ||
|
|
524849f6a9 | ||
|
|
0a64a9abe0 | ||
|
|
adb4127f5a | ||
|
|
f463593180 | ||
|
|
e53f257977 | ||
|
|
87199e856a | ||
|
|
bcf7cfbf76 | ||
|
|
9289e1388f | ||
|
|
84db9b7bb8 | ||
|
|
bd1098a181 | ||
|
|
6fd3db0063 | ||
|
|
bf52217cd5 | ||
|
|
b0cf5b3c61 | ||
|
|
347c1675ca | ||
|
|
961ae2d34f | ||
|
|
14827c3adc | ||
|
|
4a88453abc | ||
|
|
bc33d38f5e | ||
|
|
657a5ede86 | ||
|
|
dd046fe3e8 | ||
|
|
7391dbf5b1 | ||
|
|
a0ccb86bd4 | ||
|
|
c0eb5b165d | ||
|
|
9812babc7e | ||
|
|
c61c4570b0 | ||
|
|
d618feffff | ||
|
|
0fdc3c93d3 | ||
|
|
d6e76f7805 | ||
|
|
b05afe1085 | ||
|
|
2a4911f5f8 | ||
|
|
208074d845 | ||
|
|
616ff2c3fe | ||
|
|
987c939956 | ||
|
|
1615cc744e | ||
|
|
2d793a4b25 | ||
|
|
aeeb4d5504 | ||
|
|
daec801073 | ||
|
|
e4916a7b7c | ||
|
|
a08abd76e3 | ||
|
|
a2be709608 | ||
|
|
69002ef926 | ||
|
|
9ade5a7194 | ||
|
|
9b3f4f605b | ||
|
|
b862968d01 |
@@ -3,7 +3,7 @@ name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Validate
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
validate-hacs:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: HACS validation
|
||||
uses: "hacs/action@main"
|
||||
with:
|
||||
category: "integration"
|
||||
@@ -37,3 +37,4 @@ wheels/
|
||||
Thumbs.db
|
||||
*.psd
|
||||
*.zip
|
||||
*.txt
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ We welcome contributions to the HA Text AI project! This document will help you
|
||||
git remote add upstream https://github.com/smkrv/ha-text-ai.git
|
||||
```
|
||||
|
||||
### 2. Creating a Development Branch
|
||||
### 2. Creating a Development Branch
|
||||
|
||||
```bash
|
||||
# Update the main branch
|
||||
|
||||
@@ -2,22 +2,26 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
    [](https://opensource.org/licenses/MIT) [](https://github.com/hacs/integration)
|
||||
  [](https://opensource.org/licenses/MIT) [](https://github.com/hacs/integration)   
|
||||
|
||||
<img src="https://github.com/smkrv/ha-text-ai/blob/3e3ec45b195c92989434fde40ae110027f4ea124/misc/icons/icon.png" alt="HA Text AI" width="140"/>
|
||||
<img src="https://github.com/smkrv/ha-text-ai/blob/524849f6a945ec62c2cf6a6b7ecd9a28b37bf0fa/misc/icons/logo.jpg" alt="HA Text AI" height="160"/>
|
||||
|
||||
### Advanced AI Integration for Home Assistant with multi-provider support
|
||||
### Advanced AI Integration for Home Assistant with LLM multi-provider support
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
Transform your smart home experience with powerful AI assistance powered by multiple AI providers including OpenAI GPT and Anthropic Claude models. Get intelligent responses, automate complex scenarios, and enhance your home automation with advanced natural language processing.
|
||||
|
||||
</p>
|
||||
|
||||
---
|
||||
> [!NOTE]
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 🚧 ALPHA VERSION 🚧
|
||||
> Expect: potential bugs, frequent changes, incomplete features.
|
||||
> 🤝 Community Driven
|
||||
> 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>
|
||||
|
||||
## 🌟 Features
|
||||
|
||||
@@ -71,7 +75,7 @@ Transform your smart home experience with powerful AI assistance powered by mult
|
||||
|
||||
## 📋 Prerequisites
|
||||
|
||||
- Home Assistant 2023.11 or later
|
||||
- Home Assistant 2024.11 or later
|
||||
- Active API key from:
|
||||
- OpenAI ([Get key](https://platform.openai.com/account/api-keys))
|
||||
- Anthropic ([Get key](https://console.anthropic.com/))
|
||||
@@ -139,20 +143,61 @@ To be compatible, a provider should support:
|
||||
4. Follow the configuration steps
|
||||
|
||||
### Via YAML
|
||||
|
||||
### Platform Configuration (Global Settings)
|
||||
|
||||
```yaml
|
||||
ha_text_ai:
|
||||
api_provider: openai # or anthropic
|
||||
api_key: !secret ai_api_key
|
||||
model: gpt-4o-mini
|
||||
temperature: 0.7
|
||||
max_tokens: 1000
|
||||
request_interval: 1.0
|
||||
api_endpoint: https://api.openai.com/v1 # optional, for custom endpoints
|
||||
system_prompt: |
|
||||
api_provider: openai # Required
|
||||
api_key: !secret ai_api_key # Required
|
||||
model: gpt-4o-mini # Strongly recommended
|
||||
temperature: 0.7 # Optional
|
||||
max_tokens: 1000 # Optional
|
||||
request_interval: 1.0 # Optional
|
||||
api_endpoint: https://api.openai.com/v1 # Required
|
||||
system_prompt: | # Optional
|
||||
You are a home automation expert assistant.
|
||||
Focus on practical and efficient solutions.
|
||||
```
|
||||
|
||||
### Sensor Configuration
|
||||
|
||||
```yaml
|
||||
sensor:
|
||||
- platform: ha_text_ai
|
||||
name: "My AI Assistant" # Required, unique identifier
|
||||
api_provider: openai # Optional (inherits from platform)
|
||||
model: "gpt-4o-mini" # Optional
|
||||
temperature: 0.7 # Optional
|
||||
max_tokens: 1000 # Optional
|
||||
```
|
||||
|
||||
### 📋 Configuration Parameters
|
||||
|
||||
#### Platform Configuration
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `api_provider` | String | ✅ | - | AI service provider (openai, anthropic) |
|
||||
| `api_key` | String | ✅ | - | Authentication key for AI service |
|
||||
| `model` | String | ⚠️ | Provider default | Strongly recommended: Specific AI model to use. If not specified, the provider's default model will be used |
|
||||
| `temperature` | Float | ❌ | 0.7 | Response creativity level (0.0-2.0) |
|
||||
| `max_tokens` | Integer | ❌ | 1000 | Maximum response length |
|
||||
| `request_interval` | Float | ❌ | 1.0 | Delay between API requests |
|
||||
| `api_endpoint` | URL | ⚠️ | Provider default | Custom API endpoint |
|
||||
| `system_prompt` | String | ❌ | - | Default context for AI interactions |
|
||||
|
||||
#### Sensor Configuration
|
||||
|
||||
| Parameter | Type | Required | Default | Description |
|
||||
|-----------|------|----------|---------|-------------|
|
||||
| `platform` | String | ✅ | - | Must be `ha_text_ai` |
|
||||
| `name` | String | ✅ | - | Unique sensor identifier |
|
||||
| `api_provider` | String | ❌ | Platform setting | Override global provider |
|
||||
| `model` | String | ⚠️ | Platform setting | Recommended: Override global model. If not specified, uses platform or provider default |
|
||||
| `temperature` | Float | ❌ | Platform setting | Override global temperature |
|
||||
| `max_tokens` | Integer | ❌ | Platform setting | Override global max tokens |
|
||||
|
||||
## 🛠️ Available Services
|
||||
|
||||
### ask_question
|
||||
@@ -192,6 +237,143 @@ data:
|
||||
filter_model: "gpt-4o" # optional
|
||||
```
|
||||
|
||||
### 🏷️ HA Text AI Sensor Naming Convention
|
||||
|
||||
#### Character Restrictions
|
||||
- Only lowercase letters (a-z)
|
||||
- Numbers (0-9)
|
||||
- Underscore (_)
|
||||
- Maximum length: 50 characters (including `ha_text_ai_` prefix (14 characters)
|
||||
|
||||
#### Sensor Name Structure
|
||||
```yaml
|
||||
# Always starts with 'sensor.ha_text_ai_'
|
||||
# You define only the part after the underscore
|
||||
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
|
||||
```
|
||||
|
||||
#### Response Retrieval
|
||||
```yaml
|
||||
# Use your specific sensor name
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'response') }}
|
||||
```
|
||||
|
||||
#### Practical Usage
|
||||
```yaml
|
||||
automation:
|
||||
- alias: "AI Response with Custom Sensor"
|
||||
action:
|
||||
- service: ha_text_ai.ask_question
|
||||
data:
|
||||
question: "Home automation advice"
|
||||
- service: notify.mobile
|
||||
data:
|
||||
message: >
|
||||
AI Tip:
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'response') }}
|
||||
```
|
||||
|
||||
### 💡 Naming Rules
|
||||
- Prefix is always `sensor.ha_text_ai_`
|
||||
- Add your unique identifier after the underscore
|
||||
- Use lowercase
|
||||
- No spaces allowed
|
||||
- Keep it descriptive but concise
|
||||
|
||||
### 🔍 HA Text AI Sensor Attributes
|
||||
|
||||
#### Model and Provider Information
|
||||
```yaml
|
||||
# Name of the AI model currently in use (e.g., latest version of GPT)
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Model') }} # gpt-4o
|
||||
|
||||
# Service provider for the AI model (determines API endpoint and authentication)
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Api provider') }} # openai
|
||||
|
||||
# Previous or alternative model configuration
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Last model') }} # gpt-4o
|
||||
```
|
||||
|
||||
#### System Status
|
||||
```yaml
|
||||
# Current operational readiness of the AI service API
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Api status') }} # ready
|
||||
|
||||
# Indicates if a request is currently being processed
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Is processing') }} # false
|
||||
|
||||
# Shows if the API has hit its request rate limit
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Is rate limited') }} # false
|
||||
|
||||
# Status of the specific API endpoint being used
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Endpoint status') }} # ready
|
||||
```
|
||||
|
||||
#### Performance Metrics
|
||||
```yaml
|
||||
# Total number of successfully completed API requests
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Successful requests') }} # 0
|
||||
|
||||
# Number of API requests that encountered errors
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Failed requests') }} # 0
|
||||
|
||||
# Mean time taken to receive a response from the AI service
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Average latency') }} # 0
|
||||
|
||||
# Maximum time taken for a single request-response cycle
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Max latency') }} # 0
|
||||
```
|
||||
|
||||
#### Conversation and Token Usage
|
||||
```yaml
|
||||
# Number of previous interactions stored in conversation context
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'History size') }} # 0
|
||||
|
||||
# Total number of tokens used across all interactions
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Total tokens') }} # 0
|
||||
|
||||
# Tokens used in the input prompts
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Prompt tokens') }} # 0
|
||||
|
||||
# Tokens used in the AI's generated responses
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Completion tokens') }} # 0
|
||||
```
|
||||
|
||||
#### Last Interaction Details
|
||||
```yaml
|
||||
# Most recent complete response generated by the AI service
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Response') }} # Last AI response
|
||||
|
||||
# The most recently processed user query or prompt
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Question') }} # Last asked question
|
||||
|
||||
# Precise moment when the last interaction occurred (useful for tracking and logging)
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Last timestamp') }} # Timestamp
|
||||
```
|
||||
|
||||
#### System Health
|
||||
```yaml
|
||||
# Cumulative count of all errors encountered during AI service interactions
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Total errors') }} # 0
|
||||
|
||||
# Indicates if the AI service is currently undergoing scheduled or emergency maintenance
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Is maintenance') }} # false
|
||||
|
||||
# Total continuous operational time of the AI service (in hours or days)
|
||||
{{ state_attr('sensor.ha_text_ai_gpt', 'Uptime') }} # 547,58
|
||||
```
|
||||
|
||||
### 💡 Pro Tips
|
||||
- Always check attribute existence
|
||||
- Use these attributes for monitoring and automation
|
||||
- Some values might be 0 or empty initially
|
||||
|
||||
|
||||
## 📘 FAQ
|
||||
|
||||
**Q: Which AI providers are supported?**
|
||||
@@ -232,11 +414,27 @@ Contributions welcome! Please read our [Contributing Guide](CONTRIBUTING.md).
|
||||
|
||||
MIT License - see [LICENSE](LICENSE) for details.
|
||||
|
||||
## 💡 Support the Project
|
||||
|
||||
The best support is:
|
||||
- Sharing feedback
|
||||
- Contributing ideas
|
||||
- Recommending to friends
|
||||
- Reporting issues
|
||||
- Star the repository
|
||||
|
||||
If you want to say thanks financially, you can send a small token of appreciation in USDT:
|
||||
|
||||
**USDT Wallet (TRC10/TRC20):**
|
||||
`TXC9zYHYPfWUGi4Sv4R1ctTBGScXXQk5HZ`
|
||||
|
||||
*Open-source is built by community passion!* 🚀
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
Made with ❤️ for the Home Assistant Community
|
||||
Made with ❤️ and Claude 3.5 Sonnet for the Home Assistant Community
|
||||
|
||||
[Report Bug](https://github.com/smkrv/ha-text-ai/issues) · [Request Feature](https://github.com/smkrv/ha-text-ai/issues)
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ from .const import (
|
||||
SERVICE_CLEAR_HISTORY,
|
||||
SERVICE_GET_HISTORY,
|
||||
SERVICE_SET_SYSTEM_PROMPT,
|
||||
DEFAULT_MAX_HISTORY,
|
||||
CONF_MAX_HISTORY_SIZE,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -89,7 +91,7 @@ async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool:
|
||||
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.svg')
|
||||
dest = os.path.join(dest_dir, 'icon.png')
|
||||
if not os.path.exists(dest):
|
||||
shutil.copyfile(source, dest)
|
||||
except Exception as ex:
|
||||
@@ -197,11 +199,14 @@ async def async_check_api(session, endpoint: str, headers: dict, provider: str)
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up HA Text AI from a config entry."""
|
||||
_LOGGER.debug(f"Setting up HA Text AI entry: {entry.data}")
|
||||
|
||||
try:
|
||||
if CONF_API_PROVIDER not in entry.data:
|
||||
_LOGGER.error("API provider not specified")
|
||||
raise ConfigEntryNotReady("API provider is required")
|
||||
|
||||
# Get configuration
|
||||
session = aiohttp_client.async_get_clientsession(hass)
|
||||
api_provider = entry.data.get(CONF_API_PROVIDER)
|
||||
model = entry.data.get(CONF_MODEL, DEFAULT_MODEL)
|
||||
@@ -212,6 +217,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
).rstrip('/')
|
||||
api_key = entry.data[CONF_API_KEY]
|
||||
instance_name = entry.data.get(CONF_NAME, entry.entry_id)
|
||||
request_interval = entry.data.get(CONF_REQUEST_INTERVAL, DEFAULT_REQUEST_INTERVAL)
|
||||
max_tokens = entry.data.get(CONF_MAX_TOKENS, DEFAULT_MAX_TOKENS)
|
||||
temperature = entry.data.get(CONF_TEMPERATURE, DEFAULT_TEMPERATURE)
|
||||
max_history_size = entry.data.get(CONF_MAX_HISTORY_SIZE, DEFAULT_MAX_HISTORY)
|
||||
context_messages = entry.data.get(CONF_CONTEXT_MESSAGES, DEFAULT_CONTEXT_MESSAGES)
|
||||
is_anthropic = api_provider == API_PROVIDER_ANTHROPIC
|
||||
|
||||
headers = {
|
||||
@@ -242,39 +252,33 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
hass=hass,
|
||||
client=api_client,
|
||||
model=model,
|
||||
update_interval=entry.data.get(CONF_REQUEST_INTERVAL, DEFAULT_REQUEST_INTERVAL),
|
||||
update_interval=request_interval,
|
||||
instance_name=instance_name,
|
||||
max_tokens=entry.data.get(CONF_MAX_TOKENS, DEFAULT_MAX_TOKENS),
|
||||
temperature=entry.data.get(CONF_TEMPERATURE, DEFAULT_TEMPERATURE),
|
||||
max_tokens=max_tokens,
|
||||
temperature=temperature,
|
||||
max_history_size=max_history_size,
|
||||
context_messages=context_messages,
|
||||
is_anthropic=is_anthropic,
|
||||
context_messages=entry.data.get(
|
||||
CONF_CONTEXT_MESSAGES,
|
||||
DEFAULT_CONTEXT_MESSAGES
|
||||
),
|
||||
)
|
||||
|
||||
coordinator.data = coordinator._initial_state.copy()
|
||||
_LOGGER.debug(f"Initial state set for coordinator {instance_name}")
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
_LOGGER.debug(f"Created coordinator for {instance_name}")
|
||||
|
||||
# Store coordinator
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
|
||||
_LOGGER.debug(f"Stored coordinator in hass.data[{DOMAIN}][{entry.entry_id}]")
|
||||
|
||||
# Set up platforms
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
_LOGGER.info(
|
||||
"Successfully set up %s instance '%s' with model %s",
|
||||
api_provider,
|
||||
instance_name,
|
||||
model
|
||||
)
|
||||
_LOGGER.debug(f"Setup completed for {instance_name}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as ex:
|
||||
_LOGGER.exception("Setup error: %s", str(ex))
|
||||
raise ConfigEntryNotReady(f"Setup error: {str(ex)}") from ex
|
||||
except Exception as err:
|
||||
_LOGGER.exception(f"Error setting up HA Text AI: {err}")
|
||||
raise
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
|
||||
@@ -19,6 +19,7 @@ from .const import (
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIClient:
|
||||
"""API Client for OpenAI and Anthropic."""
|
||||
|
||||
@@ -59,6 +60,7 @@ class APIClient:
|
||||
payload: Dict[str, Any],
|
||||
) -> Dict[str, Any]:
|
||||
"""Make API request with retry logic."""
|
||||
_LOGGER.debug(f"API Request: URL={url}, Payload={payload}")
|
||||
for attempt in range(API_RETRY_COUNT):
|
||||
try:
|
||||
async with timeout(API_TIMEOUT):
|
||||
@@ -66,20 +68,23 @@ class APIClient:
|
||||
url,
|
||||
json=payload,
|
||||
headers=self.headers,
|
||||
timeout=self.timeout
|
||||
timeout=self.timeout,
|
||||
) as response:
|
||||
_LOGGER.debug(f"Response status: {response.status}")
|
||||
if response.status != 200:
|
||||
error_data = await response.json()
|
||||
_LOGGER.error(f"API error: {error_data}")
|
||||
raise HomeAssistantError(f"API error: {error_data}")
|
||||
return await response.json()
|
||||
except asyncio.TimeoutError:
|
||||
_LOGGER.warning(f"Timeout on attempt {attempt + 1}")
|
||||
if attempt == API_RETRY_COUNT - 1:
|
||||
raise HomeAssistantError("API request timed out")
|
||||
await asyncio.sleep(1 * (attempt + 1))
|
||||
except Exception as e:
|
||||
_LOGGER.warning(f"API request failed on attempt {attempt + 1}: {str(e)}")
|
||||
if attempt == API_RETRY_COUNT - 1:
|
||||
raise
|
||||
_LOGGER.warning("API request failed, retrying: %s", str(e))
|
||||
await asyncio.sleep(1 * (attempt + 1))
|
||||
|
||||
async def create(
|
||||
@@ -101,6 +106,11 @@ class APIClient:
|
||||
return await self._create_openai_completion(
|
||||
model, messages, temperature, max_tokens
|
||||
)
|
||||
except (KeyError, IndexError) as e:
|
||||
if "'choices'" in str(e) or "'message'" in str(e):
|
||||
raise HomeAssistantError("Failed to get a response from the AI model. Please check your internet connection and try again later.")
|
||||
else:
|
||||
raise
|
||||
except Exception as e:
|
||||
_LOGGER.error("API request failed: %s", str(e))
|
||||
raise HomeAssistantError(f"API request failed: {str(e)}")
|
||||
@@ -125,16 +135,14 @@ class APIClient:
|
||||
return {
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"content": data["choices"][0]["message"]["content"]
|
||||
}
|
||||
"message": {"content": data["choices"][0]["message"]["content"]},
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": data["usage"]["prompt_tokens"],
|
||||
"completion_tokens": data["usage"]["completion_tokens"],
|
||||
"total_tokens": data["usage"]["total_tokens"]
|
||||
}
|
||||
"total_tokens": data["usage"]["total_tokens"],
|
||||
},
|
||||
}
|
||||
|
||||
async def _create_anthropic_completion(
|
||||
@@ -147,19 +155,24 @@ class APIClient:
|
||||
"""Create completion using Anthropic API."""
|
||||
url = f"{self.endpoint}/v1/messages"
|
||||
|
||||
# Convert messages to Anthropic format
|
||||
system_prompt = next(
|
||||
(msg["content"] for msg in messages if msg["role"] == "system"),
|
||||
None
|
||||
)
|
||||
conversation = [msg for msg in messages if msg["role"] != "system"]
|
||||
system_prompt = None
|
||||
filtered_messages = []
|
||||
for msg in messages:
|
||||
if msg['role'] == 'system':
|
||||
if system_prompt is None:
|
||||
system_prompt = msg['content']
|
||||
else:
|
||||
system_prompt += f" {msg['content']}"
|
||||
else:
|
||||
filtered_messages.append(msg)
|
||||
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": conversation,
|
||||
"messages": filtered_messages,
|
||||
"max_tokens": max_tokens,
|
||||
"temperature": temperature,
|
||||
}
|
||||
|
||||
if system_prompt:
|
||||
payload["system"] = system_prompt
|
||||
|
||||
@@ -167,14 +180,26 @@ class APIClient:
|
||||
return {
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"content": data["content"][0]["text"]
|
||||
}
|
||||
"message": {"content": data["content"][0]["text"]},
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": data["usage"]["input_tokens"],
|
||||
"completion_tokens": data["usage"]["output_tokens"],
|
||||
"total_tokens": data["usage"]["input_tokens"] + data["usage"]["output_tokens"]
|
||||
}
|
||||
"total_tokens": data["usage"]["input_tokens"] + data["usage"]["output_tokens"],
|
||||
},
|
||||
}
|
||||
|
||||
async def check_connection(self) -> bool:
|
||||
"""Check API connection."""
|
||||
try:
|
||||
await self._make_request(self.endpoint, {"test": "connection"})
|
||||
return True
|
||||
except Exception as e:
|
||||
_LOGGER.error(f"Connection check failed: {str(e)}")
|
||||
return False
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
"""Shutdown API client."""
|
||||
_LOGGER.debug("Shutting down API client")
|
||||
await self.session.close()
|
||||
|
||||
@@ -34,10 +34,21 @@ from .const import (
|
||||
MIN_MAX_TOKENS,
|
||||
MAX_MAX_TOKENS,
|
||||
MIN_REQUEST_INTERVAL,
|
||||
DEFAULT_NAME_PREFIX,
|
||||
DEFAULT_MAX_HISTORY,
|
||||
CONF_MAX_HISTORY_SIZE,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def normalize_name(name: str) -> str:
|
||||
"""Normalize name to conform to HA naming convention using underscores."""
|
||||
normalized = ''.join(c if c.isalnum() or c == '_' else '_' for c in name)
|
||||
normalized = '_'.join(filter(None, normalized.split('_')))
|
||||
return normalized.lower()
|
||||
|
||||
|
||||
class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for HA text AI."""
|
||||
|
||||
@@ -69,18 +80,18 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
async def async_step_provider(self, user_input: Optional[Dict[str, Any]] = None) -> FlowResult:
|
||||
"""Handle provider configuration step."""
|
||||
self._errors = {}
|
||||
|
||||
if user_input is None:
|
||||
default_endpoint = (
|
||||
DEFAULT_OPENAI_ENDPOINT if self._provider == API_PROVIDER_OPENAI
|
||||
else DEFAULT_ANTHROPIC_ENDPOINT
|
||||
)
|
||||
|
||||
suggested_name = f"HA Text AI {len(self._async_current_entries()) + 1}"
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="provider",
|
||||
data_schema=vol.Schema({
|
||||
vol.Required(CONF_NAME, default=suggested_name): str,
|
||||
vol.Required(CONF_NAME, default="my_assistant"): str,
|
||||
vol.Required(CONF_API_KEY): str,
|
||||
vol.Required(CONF_MODEL, default=DEFAULT_MODEL): str,
|
||||
vol.Required(CONF_API_ENDPOINT, default=default_endpoint): str,
|
||||
@@ -103,27 +114,86 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
vol.Coerce(int),
|
||||
vol.Range(min=1, max=20)
|
||||
),
|
||||
}),
|
||||
errors=self._errors
|
||||
vol.Optional(
|
||||
CONF_MAX_HISTORY_SIZE,
|
||||
default=DEFAULT_MAX_HISTORY
|
||||
): vol.All(
|
||||
vol.Coerce(int),
|
||||
vol.Range(min=1, max=100)
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
instance_name = user_input[CONF_NAME]
|
||||
await self._async_validate_name(instance_name)
|
||||
if self._errors:
|
||||
return await self.async_step_provider()
|
||||
input_copy = user_input.copy()
|
||||
|
||||
if not await self._async_validate_api(user_input):
|
||||
return await self.async_step_provider()
|
||||
try:
|
||||
normalized_name = self._validate_and_normalize_name(input_copy[CONF_NAME])
|
||||
input_copy[CONF_NAME] = normalized_name
|
||||
except ValueError as e:
|
||||
return self.async_show_form(
|
||||
step_id="provider",
|
||||
data_schema=vol.Schema({
|
||||
vol.Required(CONF_NAME, default=input_copy[CONF_NAME]): str,
|
||||
vol.Required(CONF_API_KEY, default=input_copy[CONF_API_KEY]): str,
|
||||
vol.Required(CONF_MODEL, default=input_copy[CONF_MODEL]): str,
|
||||
vol.Required(CONF_API_ENDPOINT, default=input_copy[CONF_API_ENDPOINT]): str,
|
||||
vol.Optional(CONF_TEMPERATURE, default=input_copy.get(CONF_TEMPERATURE, DEFAULT_TEMPERATURE)): vol.All(
|
||||
vol.Coerce(float),
|
||||
vol.Range(min=MIN_TEMPERATURE, max=MAX_TEMPERATURE)
|
||||
),
|
||||
}),
|
||||
errors={"name": str(e)}
|
||||
)
|
||||
|
||||
return await self._create_entry(user_input)
|
||||
try:
|
||||
if not await self._async_validate_api(input_copy):
|
||||
return self.async_show_form(
|
||||
step_id="provider",
|
||||
data_schema=vol.Schema({
|
||||
}),
|
||||
errors=self._errors
|
||||
)
|
||||
except Exception as e:
|
||||
return self.async_show_form(
|
||||
step_id="provider",
|
||||
data_schema=vol.Schema({
|
||||
}),
|
||||
errors={"base": str(e)}
|
||||
)
|
||||
|
||||
return await self._create_entry(input_copy)
|
||||
|
||||
def _validate_and_normalize_name(self, name: str) -> str:
|
||||
"""
|
||||
Validate and normalize name with detailed error handling.
|
||||
|
||||
Raises:
|
||||
ValueError: If name is invalid
|
||||
|
||||
Returns:
|
||||
Normalized name
|
||||
"""
|
||||
if not name:
|
||||
raise ValueError("empty")
|
||||
|
||||
name = name.strip()
|
||||
normalized = ''.join(
|
||||
c if c.isalnum() or c in ' _' else '_' # Only allow underscores
|
||||
for c in name
|
||||
)
|
||||
|
||||
normalized = normalized.replace(' ', '_').lower()
|
||||
|
||||
async def _async_validate_name(self, name: str) -> bool:
|
||||
"""Validate that the name is unique."""
|
||||
for entry in self._async_current_entries():
|
||||
if entry.data.get(CONF_NAME) == name:
|
||||
self._errors["name"] = "name_exists"
|
||||
return False
|
||||
return True
|
||||
if entry.data.get(CONF_NAME, "") == normalized:
|
||||
raise ValueError("name_exists")
|
||||
|
||||
normalized = normalized[:50]
|
||||
|
||||
if not normalized:
|
||||
raise ValueError("empty")
|
||||
|
||||
return normalized
|
||||
|
||||
async def _async_validate_api(self, user_input: Dict[str, Any]) -> bool:
|
||||
"""Validate API connection."""
|
||||
@@ -167,21 +237,36 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
}
|
||||
|
||||
async def _create_entry(self, user_input: Dict[str, Any]) -> FlowResult:
|
||||
"""Create the config entry."""
|
||||
"""Create the config entry with comprehensive data preservation."""
|
||||
instance_name = user_input[CONF_NAME]
|
||||
unique_id = f"{DOMAIN}_{instance_name}_{self._provider}".lower().replace(" ", "_")
|
||||
normalized_name = normalize_name(instance_name)
|
||||
|
||||
unique_id = f"{DOMAIN}_{normalized_name}_{self._provider}".lower()
|
||||
|
||||
entry_data = {
|
||||
CONF_API_PROVIDER: self._provider,
|
||||
CONF_NAME: instance_name,
|
||||
"normalized_name": normalized_name,
|
||||
CONF_API_KEY: user_input.get(CONF_API_KEY),
|
||||
CONF_API_ENDPOINT: user_input.get(CONF_API_ENDPOINT),
|
||||
"unique_id": unique_id,
|
||||
CONF_MODEL: user_input.get(CONF_MODEL, DEFAULT_MODEL),
|
||||
CONF_TEMPERATURE: user_input.get(CONF_TEMPERATURE, DEFAULT_TEMPERATURE),
|
||||
CONF_MAX_TOKENS: user_input.get(CONF_MAX_TOKENS, DEFAULT_MAX_TOKENS),
|
||||
CONF_REQUEST_INTERVAL: user_input.get(CONF_REQUEST_INTERVAL, DEFAULT_REQUEST_INTERVAL),
|
||||
CONF_CONTEXT_MESSAGES: user_input.get(CONF_CONTEXT_MESSAGES, DEFAULT_CONTEXT_MESSAGES),
|
||||
CONF_MAX_HISTORY_SIZE: user_input.get(CONF_MAX_HISTORY_SIZE, DEFAULT_MAX_HISTORY),
|
||||
}
|
||||
|
||||
for key, value in user_input.items():
|
||||
if key not in entry_data:
|
||||
entry_data[key] = value
|
||||
|
||||
_LOGGER.debug(f"Creating config entry with data: {entry_data}")
|
||||
|
||||
return self.async_create_entry(
|
||||
title=instance_name,
|
||||
data={
|
||||
CONF_API_PROVIDER: self._provider,
|
||||
CONF_NAME: instance_name,
|
||||
**user_input,
|
||||
"unique_id": unique_id,
|
||||
CONF_CONTEXT_MESSAGES: user_input.get(
|
||||
CONF_CONTEXT_MESSAGES,
|
||||
DEFAULT_CONTEXT_MESSAGES)
|
||||
}
|
||||
data=entry_data
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -243,5 +328,15 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
vol.Coerce(int),
|
||||
vol.Range(min=1, max=20)
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_MAX_HISTORY_SIZE,
|
||||
default=current_data.get(
|
||||
CONF_MAX_HISTORY_SIZE,
|
||||
DEFAULT_MAX_HISTORY
|
||||
)
|
||||
): vol.All(
|
||||
vol.Coerce(int),
|
||||
vol.Range(min=1, max=100)
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ from homeassistant.helpers import config_validation as cv
|
||||
|
||||
# Domain and platforms
|
||||
DOMAIN: Final = "ha_text_ai"
|
||||
PLATFORMS: Final = [Platform.SENSOR]
|
||||
PLATFORMS: list[str] = ["sensor"]
|
||||
|
||||
# Provider configuration
|
||||
CONF_API_PROVIDER: Final = "api_provider"
|
||||
@@ -29,7 +29,7 @@ CONF_MAX_TOKENS: Final = "max_tokens"
|
||||
CONF_API_ENDPOINT: Final = "api_endpoint"
|
||||
CONF_REQUEST_INTERVAL: Final = "request_interval"
|
||||
CONF_INSTANCE: Final = "instance"
|
||||
CONF_MAX_HISTORY_SIZE: Final = "max_history_size"
|
||||
CONF_MAX_HISTORY_SIZE: Final = "max_history_size" # Correct constant name
|
||||
CONF_IS_ANTHROPIC: Final = "is_anthropic"
|
||||
CONF_CONTEXT_MESSAGES: Final = "context_messages"
|
||||
|
||||
@@ -41,6 +41,7 @@ DEFAULT_REQUEST_INTERVAL: Final = 1.0
|
||||
DEFAULT_TIMEOUT: Final = 30
|
||||
DEFAULT_MAX_HISTORY: Final = 50
|
||||
DEFAULT_NAME: Final = "HA Text AI"
|
||||
DEFAULT_NAME_PREFIX = "ha_text_ai"
|
||||
DEFAULT_CONTEXT_MESSAGES: Final = 5
|
||||
|
||||
# Parameter constraints
|
||||
@@ -164,7 +165,7 @@ SERVICE_SCHEMA_GET_HISTORY = vol.Schema({
|
||||
vol.Required(CONF_INSTANCE): cv.string,
|
||||
vol.Optional("limit", default=10): vol.All(
|
||||
vol.Coerce(int),
|
||||
vol.Range(min=1, max=100)
|
||||
vol.Range(min=1, max=100),
|
||||
),
|
||||
vol.Optional("filter_model"): cv.string
|
||||
})
|
||||
@@ -189,7 +190,7 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
vol.Coerce(float),
|
||||
vol.Range(min=MIN_REQUEST_INTERVAL, max=MAX_REQUEST_INTERVAL)
|
||||
),
|
||||
vol.Optional(CONF_MAX_HISTORY_SIZE, default=DEFAULT_MAX_HISTORY): vol.All(
|
||||
vol.Optional(CONF_MAX_HISTORY_SIZE, default=DEFAULT_MAX_HISTORY): vol.All( # Correct usage
|
||||
vol.Coerce(int),
|
||||
vol.Range(min=1, max=100),
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
@@ -9,6 +10,8 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.const import CONF_NAME
|
||||
from .config_flow import normalize_name
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
@@ -21,11 +24,16 @@ from .const import (
|
||||
DEFAULT_TEMPERATURE,
|
||||
DEFAULT_MAX_HISTORY,
|
||||
DEFAULT_CONTEXT_MESSAGES,
|
||||
DEFAULT_NAME_PREFIX,
|
||||
CONF_MAX_HISTORY_SIZE,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HATextAICoordinator(DataUpdateCoordinator):
|
||||
"""The HA Text AI coordinator."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
@@ -41,6 +49,12 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
) -> None:
|
||||
"""Initialize coordinator."""
|
||||
self.instance_name = instance_name
|
||||
self.normalized_name = None
|
||||
|
||||
# Use the normalize_name function from config_flow to ensure consistency
|
||||
from .config_flow import normalize_name
|
||||
self.normalized_name = normalize_name(instance_name)
|
||||
|
||||
self.hass = hass
|
||||
self.client = client
|
||||
self.model = model
|
||||
@@ -61,7 +75,7 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
"total_errors": 0,
|
||||
"average_latency": 0,
|
||||
"max_latency": 0,
|
||||
"min_latency": float('inf'),
|
||||
"min_latency": float("inf"),
|
||||
},
|
||||
"last_response": {
|
||||
"timestamp": dt_util.utcnow().isoformat(),
|
||||
@@ -69,7 +83,8 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
"response": "",
|
||||
"model": model,
|
||||
"instance": instance_name,
|
||||
"error": None
|
||||
"normalized_name": self.normalized_name,
|
||||
"error": None,
|
||||
},
|
||||
"is_processing": False,
|
||||
"is_rate_limited": False,
|
||||
@@ -105,13 +120,17 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
self.last_response = self._initial_state["last_response"].copy()
|
||||
self._start_time = dt_util.utcnow()
|
||||
|
||||
_LOGGER.info(f"Initialized HA Text AI coordinator with instance: {instance_name}")
|
||||
_LOGGER.info(
|
||||
f"Initialized HA Text AI coordinator with instance: {instance_name}"
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> Dict[str, Any]:
|
||||
"""Update data via library."""
|
||||
try:
|
||||
current_state = self._get_current_state()
|
||||
_LOGGER.debug(f"Updating data for {self.instance_name}, current state: {current_state}")
|
||||
_LOGGER.debug(
|
||||
f"Updating data for {self.instance_name}, current state: {current_state}"
|
||||
)
|
||||
|
||||
data = {
|
||||
"state": current_state,
|
||||
@@ -125,6 +144,7 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
"system_prompt": self._system_prompt,
|
||||
"history_size": len(self._conversation_history),
|
||||
"conversation_history": self._conversation_history,
|
||||
"normalized_name": self.normalized_name,
|
||||
}
|
||||
|
||||
# Validate data
|
||||
@@ -141,12 +161,15 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
async def async_update_ha_state(self) -> None:
|
||||
"""Update Home Assistant state."""
|
||||
try:
|
||||
_LOGGER.debug(f"Requesting state update for {self.instance_name}")
|
||||
_LOGGER.debug(
|
||||
f"Requesting state update for {self.instance_name} (normalized: {self.normalized_name})"
|
||||
)
|
||||
await self.async_request_refresh()
|
||||
|
||||
# Force update of all entities
|
||||
entity_id_base = f"sensor.ha_text_ai_{self.normalized_name.lower()}"
|
||||
for entity_id in self.hass.states.async_entity_ids():
|
||||
if entity_id.startswith(f"sensor.ha_text_ai_{self.instance_name}"):
|
||||
if entity_id.startswith(entity_id_base):
|
||||
self.hass.states.async_set(entity_id, self._get_current_state())
|
||||
|
||||
except Exception as err:
|
||||
@@ -164,15 +187,67 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
return STATE_ERROR
|
||||
return STATE_READY
|
||||
|
||||
def _calculate_context_tokens(self, messages: List[Dict[str, str]]) -> int:
|
||||
def _calculate_context_tokens(self, messages: List[Dict[str, str]], model: str = None) -> int:
|
||||
"""
|
||||
Estimate tokens for conversation context.
|
||||
|
||||
Args:
|
||||
messages: List of message dictionaries
|
||||
model: Optional model name for provider-specific estimation
|
||||
|
||||
Returns:
|
||||
Estimated number of tokens
|
||||
"""
|
||||
try:
|
||||
# Anthropic specific token counting
|
||||
if self.is_anthropic and hasattr(self.client, 'count_tokens'):
|
||||
return sum(self.client.count_tokens(msg['content']) for msg in messages)
|
||||
|
||||
return sum(len(msg['content']) // 4 for msg in messages)
|
||||
def estimate_tokens(text: str) -> int:
|
||||
"""
|
||||
Flexible token estimation algorithm.
|
||||
|
||||
Heuristics:
|
||||
- Count words
|
||||
- Estimate special characters
|
||||
- Fallback to character-based estimation
|
||||
"""
|
||||
# Word-based estimation
|
||||
words = len(text.split())
|
||||
|
||||
# Special character handling
|
||||
special_chars = sum(1 for char in text if not char.isalnum())
|
||||
|
||||
# Character-based fallback
|
||||
char_tokens = len(text) // 4
|
||||
|
||||
# Combine estimations with bias towards words
|
||||
total_tokens = (words * 1.5) + (special_chars * 0.5) + char_tokens
|
||||
|
||||
return max(int(total_tokens), words)
|
||||
|
||||
# Calculate total tokens across all messages
|
||||
total_tokens = sum(estimate_tokens(msg['content']) for msg in messages)
|
||||
|
||||
# Logging for debugging
|
||||
_LOGGER.debug(
|
||||
f"Token Estimation: "
|
||||
f"Messages: {len(messages)}, "
|
||||
f"Estimated Tokens: {total_tokens}"
|
||||
)
|
||||
|
||||
return total_tokens
|
||||
|
||||
except Exception as e:
|
||||
_LOGGER.warning(f"Error calculating context tokens: {e}")
|
||||
return 0
|
||||
# Safe fallback with detailed logging
|
||||
_LOGGER.warning(
|
||||
f"Token estimation failed. "
|
||||
f"Error: {e}. "
|
||||
f"Using conservative estimation."
|
||||
)
|
||||
|
||||
# Conservative token estimation
|
||||
return len(messages) * 100
|
||||
|
||||
async def async_ask_question(
|
||||
self,
|
||||
@@ -183,80 +258,133 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
system_prompt: Optional[str] = None,
|
||||
context_messages: Optional[int] = None,
|
||||
) -> dict:
|
||||
"""Process a question with optional parameters."""
|
||||
"""
|
||||
Process a question with optional parameters.
|
||||
|
||||
This method is a direct wrapper around async_process_question,
|
||||
allowing flexible AI interaction with optional model, temperature,
|
||||
and context customization.
|
||||
|
||||
Args:
|
||||
question: The input question or prompt
|
||||
model: Optional AI model to use
|
||||
temperature: Optional response creativity level
|
||||
max_tokens: Optional maximum response length
|
||||
system_prompt: Optional system-level instruction
|
||||
context_messages: Optional number of context messages to include
|
||||
|
||||
Returns:
|
||||
Full response dictionary from the AI
|
||||
"""
|
||||
return await self.async_process_question(
|
||||
question, model, temperature, max_tokens, system_prompt, context_messages
|
||||
)
|
||||
|
||||
async def async_process_question(
|
||||
self,
|
||||
question: str,
|
||||
model: Optional[str] = None,
|
||||
temperature: Optional[float] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
system_prompt: Optional[str] = None,
|
||||
context_messages: Optional[int] = None,
|
||||
) -> dict:
|
||||
temp_context_messages = context_messages or self.context_messages
|
||||
self,
|
||||
question: str,
|
||||
model: Optional[str] = None,
|
||||
temperature: Optional[float] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
system_prompt: Optional[str] = None,
|
||||
context_messages: Optional[int] = None,
|
||||
) -> dict:
|
||||
"""
|
||||
Enhanced question processing with intelligent token management.
|
||||
"""
|
||||
try:
|
||||
self._is_processing = True
|
||||
await self.async_update_ha_state()
|
||||
|
||||
if not question:
|
||||
raise ValueError("Question cannot be empty")
|
||||
temp_context_messages = context_messages or self.context_messages
|
||||
temp_model = model or self.model
|
||||
temp_temperature = temperature or self.temperature
|
||||
temp_max_tokens = max_tokens or self.max_tokens
|
||||
temp_system_prompt = system_prompt or self._system_prompt
|
||||
|
||||
_LOGGER.debug(f"Processing question for instance {self.instance_name}")
|
||||
# Start timing
|
||||
start_time = dt_util.utcnow()
|
||||
|
||||
try:
|
||||
self._is_processing = True
|
||||
await self.async_update_ha_state()
|
||||
|
||||
temp_model = model or self.model
|
||||
temp_temperature = temperature or self.temperature
|
||||
temp_max_tokens = max_tokens or self.max_tokens
|
||||
temp_system_prompt = system_prompt or self._system_prompt
|
||||
|
||||
start_time = dt_util.utcnow()
|
||||
|
||||
messages = []
|
||||
if temp_system_prompt:
|
||||
if self.is_anthropic:
|
||||
system_content = f"\n\nHuman: {temp_system_prompt}\n\nAssistant: I understand and will follow these instructions."
|
||||
messages.append({"role": "user", "content": system_content})
|
||||
else:
|
||||
# Prepare messages with system prompt
|
||||
messages = []
|
||||
if temp_system_prompt:
|
||||
messages.append({"role": "system", "content": temp_system_prompt})
|
||||
|
||||
# Add conversation history
|
||||
context_history = self._conversation_history[-temp_context_messages:]
|
||||
for entry in context_history:
|
||||
messages.append({"role": "user", "content": entry["question"]})
|
||||
messages.append({"role": "assistant", "content": entry["response"]})
|
||||
# Context history management
|
||||
context_history = self._conversation_history[-temp_context_messages:]
|
||||
|
||||
messages.append({"role": "user", "content": question})
|
||||
# Comprehensive token calculation
|
||||
context_tokens = self._calculate_context_tokens(
|
||||
[{"content": entry["question"]} for entry in context_history] +
|
||||
[{"content": entry["response"]} for entry in context_history] +
|
||||
[{"content": question}],
|
||||
temp_model
|
||||
)
|
||||
|
||||
kwargs = {
|
||||
"model": temp_model,
|
||||
"temperature": temp_temperature,
|
||||
"max_tokens": temp_max_tokens,
|
||||
"messages": messages,
|
||||
}
|
||||
# Dynamic token allocation
|
||||
available_tokens = max(0, temp_max_tokens - context_tokens)
|
||||
|
||||
response = await self.async_process_message(question, **kwargs)
|
||||
# Context trimming if over token limit
|
||||
if context_tokens > temp_max_tokens:
|
||||
_LOGGER.warning(
|
||||
f"Token limit exceeded. "
|
||||
f"Context: {context_tokens}, "
|
||||
f"Max: {temp_max_tokens}"
|
||||
)
|
||||
|
||||
# Update metrics
|
||||
end_time = dt_util.utcnow()
|
||||
latency = (end_time - start_time).total_seconds()
|
||||
self._update_metrics(latency, response)
|
||||
# Intelligent context reduction
|
||||
while context_tokens > temp_max_tokens // 2 and context_history:
|
||||
context_history.pop(0)
|
||||
context_tokens = self._calculate_context_tokens(
|
||||
[{"content": entry["question"]} for entry in context_history] +
|
||||
[{"content": entry["response"]} for entry in context_history] +
|
||||
[{"content": question}],
|
||||
temp_model
|
||||
)
|
||||
|
||||
# Update history
|
||||
self._update_history(question, response)
|
||||
# Rebuild messages with trimmed context
|
||||
for entry in context_history:
|
||||
messages.append({"role": "user", "content": entry["question"]})
|
||||
messages.append({"role": "assistant", "content": entry["response"]})
|
||||
|
||||
return response
|
||||
messages.append({"role": "user", "content": question})
|
||||
|
||||
except Exception as err:
|
||||
self._handle_error(err)
|
||||
raise HomeAssistantError(f"Failed to process question: {err}")
|
||||
# Detailed token logging
|
||||
_LOGGER.debug(
|
||||
f"Token Analysis: "
|
||||
f"Context Tokens: {context_tokens}, "
|
||||
f"Max Tokens: {temp_max_tokens}, "
|
||||
f"Available Tokens: {available_tokens}"
|
||||
)
|
||||
|
||||
finally:
|
||||
self._is_processing = False
|
||||
await self.async_update_ha_state()
|
||||
# Prepare API call with dynamic token management
|
||||
kwargs = {
|
||||
"model": temp_model,
|
||||
"temperature": temp_temperature,
|
||||
"max_tokens": min(temp_max_tokens, available_tokens),
|
||||
"messages": messages,
|
||||
}
|
||||
|
||||
# Process message
|
||||
response = await self.async_process_message(question, **kwargs)
|
||||
|
||||
# Update metrics
|
||||
end_time = dt_util.utcnow()
|
||||
latency = (end_time - start_time).total_seconds()
|
||||
self._update_metrics(latency, response)
|
||||
|
||||
# Update history
|
||||
self._update_history(question, response)
|
||||
|
||||
return response
|
||||
|
||||
except Exception as err:
|
||||
self._handle_error(err)
|
||||
raise HomeAssistantError(f"Failed to process question: {err}")
|
||||
|
||||
finally:
|
||||
self._is_processing = False
|
||||
await self.async_update_ha_state()
|
||||
|
||||
async def async_process_message(self, question: str, **kwargs) -> dict:
|
||||
"""Process message using the AI client."""
|
||||
@@ -272,7 +400,7 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
"response": response["content"],
|
||||
"model": kwargs.get("model", self.model),
|
||||
"instance": self.instance_name,
|
||||
"error": None
|
||||
"error": None,
|
||||
}
|
||||
|
||||
return response
|
||||
@@ -283,20 +411,26 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
|
||||
async def _process_anthropic_message(self, question: str, **kwargs) -> dict:
|
||||
"""Process message using Anthropic API."""
|
||||
response = await self.client.messages.create(
|
||||
model=kwargs["model"],
|
||||
max_tokens=kwargs["max_tokens"],
|
||||
messages=kwargs["messages"],
|
||||
temperature=kwargs["temperature"],
|
||||
)
|
||||
return {
|
||||
"content": response.content[0].text,
|
||||
"tokens": {
|
||||
"prompt": response.usage.input_tokens,
|
||||
"completion": response.usage.output_tokens,
|
||||
"total": response.usage.input_tokens + response.usage.output_tokens
|
||||
try:
|
||||
_LOGGER.debug(f"Anthropic API call: model={kwargs['model']}, max_tokens={kwargs['max_tokens']}")
|
||||
response = await self.client.messages.create(
|
||||
model=kwargs["model"],
|
||||
max_tokens=kwargs["max_tokens"],
|
||||
messages=kwargs["messages"],
|
||||
temperature=kwargs["temperature"],
|
||||
)
|
||||
_LOGGER.debug(f"Anthropic response: tokens={response.usage}")
|
||||
return {
|
||||
"content": response.content[0].text,
|
||||
"tokens": {
|
||||
"prompt": response.usage.input_tokens,
|
||||
"completion": response.usage.output_tokens,
|
||||
"total": response.usage.input_tokens + response.usage.output_tokens,
|
||||
},
|
||||
}
|
||||
}
|
||||
except Exception as e:
|
||||
_LOGGER.error(f"Anthropic API error: {str(e)}")
|
||||
raise
|
||||
|
||||
async def _process_openai_message(self, question: str, **kwargs) -> dict:
|
||||
"""Process message using OpenAI API."""
|
||||
@@ -313,8 +447,8 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
"tokens": {
|
||||
"prompt": response["usage"]["prompt_tokens"],
|
||||
"completion": response["usage"]["completion_tokens"],
|
||||
"total": response["usage"]["total_tokens"]
|
||||
}
|
||||
"total": response["usage"]["total_tokens"],
|
||||
},
|
||||
}
|
||||
except Exception as e:
|
||||
_LOGGER.error(f"Error in OpenAI API call: {str(e)}")
|
||||
@@ -339,29 +473,68 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
|
||||
def _update_history(self, question: str, response: dict) -> None:
|
||||
"""Update conversation history."""
|
||||
self._conversation_history.append({
|
||||
"timestamp": dt_util.utcnow().isoformat(),
|
||||
"question": question,
|
||||
"response": response["content"]
|
||||
})
|
||||
self._conversation_history.append(
|
||||
{
|
||||
"timestamp": dt_util.utcnow().isoformat(),
|
||||
"question": question,
|
||||
"response": response["content"],
|
||||
}
|
||||
)
|
||||
|
||||
while len(self._conversation_history) > self.max_history_size:
|
||||
self._conversation_history.pop(0)
|
||||
|
||||
def _handle_error(self, error: Exception) -> None:
|
||||
"""Handle error and update metrics."""
|
||||
"""
|
||||
Enhanced error handling with comprehensive diagnostics.
|
||||
|
||||
Captures detailed error information, tracks error metrics,
|
||||
and provides context for troubleshooting AI processing issues.
|
||||
"""
|
||||
self._performance_metrics["total_errors"] += 1
|
||||
self._performance_metrics["failed_requests"] += 1
|
||||
|
||||
self.last_response = {
|
||||
error_details = {
|
||||
"timestamp": dt_util.utcnow().isoformat(),
|
||||
"question": "",
|
||||
"response": "",
|
||||
"model": self.model,
|
||||
"instance": self.instance_name,
|
||||
"error": str(error)
|
||||
"error_message": str(error),
|
||||
"error_type": type(error).__name__,
|
||||
"traceback": traceback.format_exc() if _LOGGER.isEnabledFor(logging.DEBUG) else None,
|
||||
}
|
||||
|
||||
# Specific error type handling
|
||||
error_mapping = {
|
||||
HomeAssistantError: {"is_ha_error": True},
|
||||
ConnectionError: {
|
||||
"is_connection_error": True,
|
||||
"is_rate_limited": True
|
||||
},
|
||||
TimeoutError: {"is_timeout": True},
|
||||
PermissionError: {"is_permission_denied": True},
|
||||
ValueError: {"is_validation_error": True}
|
||||
}
|
||||
|
||||
for error_type, error_flags in error_mapping.items():
|
||||
if isinstance(error, error_type):
|
||||
error_details.update(error_flags)
|
||||
break
|
||||
|
||||
# Update system state based on error type
|
||||
if error_details.get("is_rate_limited"):
|
||||
self._is_rate_limited = True
|
||||
_LOGGER.warning(f"Rate limit detected for {self.instance_name}")
|
||||
|
||||
if error_details.get("is_connection_error"):
|
||||
self.endpoint_status = "unavailable"
|
||||
|
||||
self.last_response = error_details
|
||||
_LOGGER.error(f"AI Processing Error: {error_details}")
|
||||
|
||||
# Optional: Add more sophisticated error tracking or notification logic
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
_LOGGER.debug(f"Full Error Traceback: {error_details['traceback']}")
|
||||
|
||||
async def async_clear_history(self) -> None:
|
||||
"""Clear conversation history."""
|
||||
self._conversation_history = []
|
||||
@@ -375,3 +548,8 @@ class HATextAICoordinator(DataUpdateCoordinator):
|
||||
"""Set system prompt."""
|
||||
self._system_prompt = prompt
|
||||
await self.async_update_ha_state()
|
||||
|
||||
async def async_shutdown(self) -> None:
|
||||
"""Shutdown coordinator."""
|
||||
_LOGGER.debug(f"Shutting down coordinator for {self.instance_name}")
|
||||
self.hass.data[DOMAIN].pop(self.instance_name, None)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 678 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -23,6 +23,6 @@
|
||||
"single_config_entry": false,
|
||||
"ssdp": [],
|
||||
"usb": [],
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.3-beta",
|
||||
"zeroconf": []
|
||||
}
|
||||
|
||||
@@ -58,25 +58,39 @@ from .const import (
|
||||
ENTITY_ICON,
|
||||
ENTITY_ICON_ERROR,
|
||||
ENTITY_ICON_PROCESSING,
|
||||
DEFAULT_NAME_PREFIX,
|
||||
CONF_MAX_HISTORY_SIZE,
|
||||
)
|
||||
|
||||
from .coordinator import HATextAICoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the HA Text AI sensor."""
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
instance_name = coordinator.instance_name
|
||||
_LOGGER.debug(f"Starting sensor setup for entry: {entry.entry_id}")
|
||||
|
||||
_LOGGER.debug(f"Setting up sensor with instance: {instance_name}")
|
||||
try:
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||
_LOGGER.debug(f"Found coordinator for entry {entry.entry_id}")
|
||||
|
||||
sensor = HATextAISensor(coordinator, entry)
|
||||
async_add_entities([sensor], True)
|
||||
instance_name = coordinator.instance_name
|
||||
_LOGGER.debug(f"Setting up sensor with instance: {instance_name}")
|
||||
|
||||
sensor = HATextAISensor(coordinator, entry)
|
||||
_LOGGER.debug(f"Created sensor instance: {sensor.entity_id}")
|
||||
|
||||
async_add_entities([sensor], True)
|
||||
_LOGGER.debug(f"Added sensor entity: {sensor.entity_id}")
|
||||
|
||||
except Exception as err:
|
||||
_LOGGER.exception(f"Error setting up sensor: {err}")
|
||||
raise
|
||||
|
||||
class HATextAISensor(CoordinatorEntity, SensorEntity):
|
||||
"""HA Text AI Sensor."""
|
||||
@@ -89,19 +103,30 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
|
||||
config_entry: ConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
_LOGGER.debug(f"Initializing sensor with config entry: {config_entry.data}")
|
||||
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._config_entry = config_entry
|
||||
self._instance_name = coordinator.instance_name
|
||||
self._normalized_name = coordinator.normalized_name
|
||||
|
||||
_LOGGER.debug(f"Instance name: {self._instance_name}")
|
||||
_LOGGER.debug(f"Normalized name: {self._normalized_name}")
|
||||
|
||||
self._conversation_history = []
|
||||
self._system_prompt = None
|
||||
|
||||
self._attr_name = f"HA Text AI {self._instance_name}"
|
||||
self.entity_id = f"sensor.ha_text_ai_{slugify(self._instance_name)}"
|
||||
self.entity_id = f"sensor.ha_text_ai_{self._normalized_name}"
|
||||
self._attr_unique_id = f"{config_entry.entry_id}"
|
||||
|
||||
_LOGGER.debug(f"Created sensor with entity_id: {self.entity_id}")
|
||||
_LOGGER.debug(f"Sensor name: {self._attr_name}")
|
||||
_LOGGER.debug(f"Unique ID: {self._attr_unique_id}")
|
||||
|
||||
self.entity_description = SensorEntityDescription(
|
||||
key=f"ha_text_ai_{self._instance_name}",
|
||||
key=f"ha_text_ai_{self._normalized_name.lower()}",
|
||||
entity_registry_enabled_default=True,
|
||||
)
|
||||
|
||||
@@ -118,13 +143,15 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._attr_unique_id)},
|
||||
name=self._attr_name, # Используем имя сенсора
|
||||
name=self._attr_name,
|
||||
manufacturer="Community",
|
||||
model=f"{model} ({api_provider} provider)",
|
||||
sw_version="1.0.0",
|
||||
)
|
||||
|
||||
_LOGGER.debug(f"Initialized sensor: {self.entity_id} for instance: {self._instance_name}")
|
||||
_LOGGER.debug(
|
||||
f"Initialized sensor: {self.entity_id} for instance: {self._instance_name}"
|
||||
)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
@@ -180,11 +207,14 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
|
||||
data = self.coordinator.data
|
||||
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,
|
||||
"instance_name": self._instance_name,
|
||||
"normalized_name": self._normalized_name,
|
||||
ATTR_SYSTEM_PROMPT: data.get("system_prompt"),
|
||||
ATTR_IS_PROCESSING: data.get("is_processing", False),
|
||||
ATTR_IS_RATE_LIMITED: data.get("is_rate_limited", False),
|
||||
@@ -199,32 +229,40 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
|
||||
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")),
|
||||
})
|
||||
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")),
|
||||
}
|
||||
)
|
||||
|
||||
# Add last response
|
||||
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"),
|
||||
})
|
||||
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]
|
||||
attributes[ATTR_PERFORMANCE_METRICS] = data[
|
||||
ATTR_PERFORMANCE_METRICS
|
||||
]
|
||||
|
||||
# Add API version if available
|
||||
if ATTR_API_VERSION in data:
|
||||
@@ -288,7 +326,7 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
|
||||
"Error handling update for %s: %s",
|
||||
self.entity_id,
|
||||
err,
|
||||
exc_info=True
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -40,15 +40,14 @@ ask_question:
|
||||
min: 1
|
||||
max: 20
|
||||
step: 1
|
||||
mode: box
|
||||
mode: slider
|
||||
|
||||
model:
|
||||
name: Model
|
||||
description: "Select AI model to use (optional, overrides default setting)"
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
multiline: false
|
||||
selector:
|
||||
text: {}
|
||||
|
||||
temperature:
|
||||
name: Temperature
|
||||
@@ -73,3 +72,98 @@ ask_question:
|
||||
max: 4096
|
||||
step: 1
|
||||
mode: box
|
||||
|
||||
clear_history:
|
||||
name: Clear History
|
||||
description: >-
|
||||
Delete all stored questions and responses from the conversation history
|
||||
fields:
|
||||
instance:
|
||||
name: Instance
|
||||
description: Name of the HA Text AI instance to clear history for
|
||||
required: true
|
||||
selector:
|
||||
entity:
|
||||
integration: ha_text_ai
|
||||
domain: sensor
|
||||
|
||||
get_history:
|
||||
name: Get History
|
||||
description: Retrieve conversation history with optional filtering and sorting
|
||||
fields:
|
||||
instance:
|
||||
name: Instance
|
||||
description: Name of the HA Text AI instance to get history from
|
||||
required: true
|
||||
selector:
|
||||
entity:
|
||||
integration: ha_text_ai
|
||||
domain: sensor
|
||||
|
||||
limit:
|
||||
name: Limit
|
||||
description: Number of conversations to return (1-100)
|
||||
required: false
|
||||
default: 10
|
||||
selector:
|
||||
number:
|
||||
min: 1
|
||||
max: 100
|
||||
step: 1
|
||||
mode: box
|
||||
|
||||
filter_model:
|
||||
name: Filter Model
|
||||
description: Filter conversations by specific AI model
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
multiline: false
|
||||
|
||||
start_date:
|
||||
name: Start Date
|
||||
description: Filter conversations starting from this date/time
|
||||
required: false
|
||||
selector:
|
||||
text:
|
||||
multiline: false
|
||||
|
||||
include_metadata:
|
||||
name: Include Metadata
|
||||
description: Include additional information like tokens used, response time, etc.
|
||||
required: false
|
||||
default: false
|
||||
selector:
|
||||
boolean:
|
||||
|
||||
sort_order:
|
||||
name: Sort Order
|
||||
description: Sort order for results (newest or oldest first)
|
||||
required: false
|
||||
default: newest
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- newest
|
||||
- oldest
|
||||
|
||||
set_system_prompt:
|
||||
name: Set System Prompt
|
||||
description: Set default system behavior instructions for all future conversations
|
||||
fields:
|
||||
instance:
|
||||
name: Instance
|
||||
description: Name of the HA Text AI instance to set system prompt for
|
||||
required: true
|
||||
selector:
|
||||
entity:
|
||||
integration: ha_text_ai
|
||||
domain: sensor
|
||||
|
||||
prompt:
|
||||
name: System Prompt
|
||||
description: Instructions that define how the AI should behave and respond
|
||||
required: true
|
||||
selector:
|
||||
text:
|
||||
multiline: true
|
||||
|
||||
@@ -1,261 +1,266 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"provider": {
|
||||
"title": "KI-Anbieter auswählen",
|
||||
"description": "Wählen Sie den KI-Dienst-Anbieter für diese Instanz",
|
||||
"data": {
|
||||
"api_provider": "API-Anbieter",
|
||||
"context_messages": "Anzahl der zu speichernden Kontextnachrichten (1-20)"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "HA Text AI Instanz konfigurieren",
|
||||
"description": "Richten Sie eine neue KI-Assistenten-Instanz mit Ihrem ausgewählten Anbieter ein",
|
||||
"data": {
|
||||
"name": "Instanzname (z.B. 'GPT Assistent', 'Claude Helfer')",
|
||||
"api_key": "API-Schlüssel für Authentifizierung",
|
||||
"model": "Zu verwendendes KI-Modell",
|
||||
"temperature": "Antwort-Kreativität (0-2, niedriger = fokussierter)",
|
||||
"max_tokens": "Maximale Antwortlänge (1-4096 Token)",
|
||||
"api_endpoint": "Benutzerdefinierte API-Endpunkt-URL (optional)",
|
||||
"request_interval": "Minimale Zeit zwischen Anfragen (0,1-60 Sekunden)",
|
||||
"context_messages": "Anzahl der zu speichernden Kontextnachrichten (1-20)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"name_exists": "Eine Instanz mit diesem Namen existiert bereits",
|
||||
"invalid_name": "Ungültiger Instanzname",
|
||||
"invalid_auth": "Authentifizierung fehlgeschlagen - überprüfen Sie Ihren API-Schlüssel",
|
||||
"invalid_api_key": "Ungültiger API-Schlüssel - bitte überprüfen Sie Ihre Anmeldedaten",
|
||||
"cannot_connect": "Verbindung zum API-Dienst fehlgeschlagen",
|
||||
"invalid_model": "Ausgewähltes Modell ist nicht verfügbar",
|
||||
"rate_limit": "Anfragelimit überschritten",
|
||||
"context_length": "Kontextlänge überschritten",
|
||||
"rate_limit_exceeded": "API-Anfragelimit überschritten",
|
||||
"maintenance": "Dienst ist in Wartung",
|
||||
"invalid_response": "Ungültige API-Antwort erhalten",
|
||||
"api_error": "API-Dienst-Fehler aufgetreten",
|
||||
"timeout": "Anfrage-Zeitüberschreitung",
|
||||
"invalid_instance": "Ungültige Instanz angegeben",
|
||||
"unknown": "Unerwarteter Fehler aufgetreten"
|
||||
"config": {
|
||||
"step": {
|
||||
"provider": {
|
||||
"title": "KI-Anbieter auswählen",
|
||||
"description": "Wählen Sie den KI-Dienstanbieter für diese Instanz aus.",
|
||||
"data": {
|
||||
"api_provider": "API-Anbieter",
|
||||
"context_messages": "Anzahl der zu behaltenden Kontextnachrichten (1-20)"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "HA Text AI-Instanz konfigurieren",
|
||||
"description": "Richten Sie eine neue KI-Assistenteninstanz mit Ihrem ausgewählten Anbieter ein.",
|
||||
"data": {
|
||||
"name": "Instanzname (z. B. 'GPT-Assistent', 'Claude-Helfer')",
|
||||
"api_key": "API-Schlüssel zur Authentifizierung",
|
||||
"model": "Zu verwendendes KI-Modell",
|
||||
"temperature": "Antwortkreativität (0-2, niedriger = fokussierter)",
|
||||
"max_tokens": "Maximale Antwortlänge (1-4096 Token)",
|
||||
"api_endpoint": "Benutzerdefinierte API-Endpunkt-URL (optional)",
|
||||
"api_provider": "API-Anbieter",
|
||||
"request_interval": "Mindestzeit zwischen Anfragen (0,1-60 Sekunden)",
|
||||
"context_messages": "Anzahl der zu behaltenden Kontextnachrichten (1-20)",
|
||||
"max_history_size": "Maximale Größe des Konversationsverlaufs (1-100)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Instanzeinstellungen aktualisieren",
|
||||
"description": "Einstellungen für diese KI-Assistenten-Instanz ändern",
|
||||
"data": {
|
||||
"model": "KI-Modell",
|
||||
"temperature": "Antwort-Kreativität (0-2)",
|
||||
"max_tokens": "Maximale Antwortlänge (1-4096)",
|
||||
"request_interval": "Minimales Anfragen-Intervall (0,1-60 Sekunden)",
|
||||
"context_messages": "Anzahl der vorherigen Nachrichten, die in den Kontext einbezogen werden sollen (1-20)"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"ask_question": {
|
||||
"name": "Frage stellen (HA Text AI)",
|
||||
"description": "Senden Sie eine Frage an das KI-Modell und erhalten Sie eine detaillierte Antwort. Die Antwort wird in der Gesprächshistorie gespeichert und kann später abgerufen werden.",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instanz",
|
||||
"description": "Name der zu verwendenden HA Text AI Instanz"
|
||||
},
|
||||
"question": {
|
||||
"name": "Frage",
|
||||
"description": "Ihre Frage oder Eingabeaufforderung für den KI-Assistenten"
|
||||
},
|
||||
"context_messages": {
|
||||
"name": "Kontextnachrichten",
|
||||
"description": "Anzahl der vorherigen Nachrichten, die in den Kontext einbezogen werden sollen (1-20)"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Systemaufforderung",
|
||||
"description": "Optionale Systemaufforderung zur Kontexteinstellung für diese spezifische Frage"
|
||||
},
|
||||
"model": {
|
||||
"name": "Modell",
|
||||
"description": "Wählen Sie das zu verwendende KI-Modell (optional, überschreibt Standardeinstellung)"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperatur",
|
||||
"description": "Steuert die Antwort-Kreativität (0,0-2,0)"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max. Token",
|
||||
"description": "Maximale Länge der Antwort (1-4096 Token)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear_history": {
|
||||
"name": "Verlauf löschen",
|
||||
"description": "Alle gespeicherten Fragen und Antworten aus dem Gesprächsverlauf löschen",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instanz",
|
||||
"description": "Name der HA Text AI Instanz, für die der Verlauf gelöscht werden soll"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_history": {
|
||||
"name": "Verlauf abrufen",
|
||||
"description": "Gesprächsverlauf mit optionaler Filterung und Sortierung abrufen",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instanz",
|
||||
"description": "Name der HA Text AI Instanz, aus der der Verlauf abgerufen werden soll"
|
||||
},
|
||||
"limit": {
|
||||
"name": "Limit",
|
||||
"description": "Anzahl der zurückzugebenden Gespräche (1-100)"
|
||||
},
|
||||
"filter_model": {
|
||||
"name": "Modell filtern",
|
||||
"description": "Gespräche nach bestimmtem KI-Modell filtern"
|
||||
},
|
||||
"start_date": {
|
||||
"name": "Startdatum",
|
||||
"description": "Gespräche ab diesem Datum/Zeitpunkt filtern"
|
||||
},
|
||||
"include_metadata": {
|
||||
"name": "Metadaten einbeziehen",
|
||||
"description": "Zusätzliche Informationen wie verwendete Token, Antwortzeit usw. einbeziehen"
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "Sortierreihenfolge",
|
||||
"description": "Sortierreihenfolge der Ergebnisse (neueste oder älteste zuerst)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_system_prompt": {
|
||||
"name": "Systemaufforderung festlegen",
|
||||
"description": "Standardmäßige Systemverhaltensinstruktionen für alle zukünftigen Gespräche festlegen",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instanz",
|
||||
"description": "Name der HA Text AI Instanz, für die die Systemaufforderung festgelegt werden soll"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "Systemaufforderung",
|
||||
"description": "Anweisungen, die definieren, wie sich die KI verhalten und antworten soll"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ha_text_ai": {
|
||||
"name": "{name}",
|
||||
"state": {
|
||||
"ready": "Bereit",
|
||||
"processing": "Verarbeitung",
|
||||
"error": "Fehler",
|
||||
"disconnected": "Getrennt",
|
||||
"rate_limited": "Anfragelimit",
|
||||
"maintenance": "Wartung",
|
||||
"initializing": "Initialisierung",
|
||||
"retrying": "Wiederholung",
|
||||
"queued": "In Warteschlange"
|
||||
},
|
||||
"state_attributes": {
|
||||
"question": {
|
||||
"name": "Letzte Frage"
|
||||
},
|
||||
"response": {
|
||||
"name": "Letzte Antwort"
|
||||
},
|
||||
"model": {
|
||||
"name": "Aktuelles Modell"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperatur"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max. Token"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Systemaufforderung"
|
||||
},
|
||||
"response_time": {
|
||||
"name": "Letzte Antwortzeit"
|
||||
},
|
||||
"total_responses": {
|
||||
"name": "Gesamte Antworten"
|
||||
},
|
||||
"error_count": {
|
||||
"name": "Fehleranzahl"
|
||||
},
|
||||
"last_error": {
|
||||
"name": "Letzter Fehler"
|
||||
},
|
||||
"api_status": {
|
||||
"name": "API-Status"
|
||||
},
|
||||
"tokens_used": {
|
||||
"name": "Insgesamt verwendete Token"
|
||||
},
|
||||
"average_response_time": {
|
||||
"name": "Durchschnittliche Antwortzeit"
|
||||
},
|
||||
"last_request_time": {
|
||||
"name": "Letzte Anforderungszeit"
|
||||
},
|
||||
"is_processing": {
|
||||
"name": "Verarbeitungsstatus"
|
||||
},
|
||||
"is_rate_limited": {
|
||||
"name": "Status Anfragelimit"
|
||||
},
|
||||
"is_maintenance": {
|
||||
"name": "Wartungsstatus"
|
||||
},
|
||||
"api_version": {
|
||||
"name": "API-Version"
|
||||
},
|
||||
"endpoint_status": {
|
||||
"name": "Endpunktstatus"
|
||||
},
|
||||
"performance_metrics": {
|
||||
"name": "Leistungsmetriken"
|
||||
},
|
||||
"history_size": {
|
||||
"name": "Verlaufsgröße"
|
||||
},
|
||||
"uptime": {
|
||||
"name": "Betriebszeit"
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "Gesamte Token"
|
||||
},
|
||||
"prompt_tokens": {
|
||||
"name": "Prompt-Token"
|
||||
},
|
||||
"completion_tokens": {
|
||||
"name": "Abschluss-Token"
|
||||
},
|
||||
"successful_requests": {
|
||||
"name": "Erfolgreiche Anfragen"
|
||||
},
|
||||
"failed_requests": {
|
||||
"name": "Fehlgeschlagene Anfragen"
|
||||
},
|
||||
"average_latency": {
|
||||
"name": "Durchschnittliche Latenz"
|
||||
},
|
||||
"max_latency": {
|
||||
"name": "Maximale Latenz"
|
||||
},
|
||||
"min_latency": {
|
||||
"name": "Minimale Latenz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"error": {
|
||||
"name_exists": "Eine Instanz mit diesem Namen existiert bereits",
|
||||
"invalid_name": "Ungültiger Instanzname",
|
||||
"invalid_auth": "Authentifizierung fehlgeschlagen - überprüfen Sie Ihren API-Schlüssel",
|
||||
"invalid_api_key": "Ungültiger API-Schlüssel - überprüfen Sie Ihre Anmeldedaten",
|
||||
"cannot_connect": "Verbindung zum API-Dienst fehlgeschlagen",
|
||||
"invalid_model": "Das ausgewählte Modell ist nicht verfügbar",
|
||||
"rate_limit": "Ratenlimit überschritten",
|
||||
"context_length": "Kontextlänge überschritten",
|
||||
"rate_limit_exceeded": "API-Ratenlimit überschritten",
|
||||
"maintenance": "Dienst befindet sich in der Wartung",
|
||||
"invalid_response": "Ungültige API-Antwort empfangen",
|
||||
"api_error": "Fehler im API-Dienst aufgetreten",
|
||||
"timeout": "Anfrage ist abgelaufen",
|
||||
"invalid_instance": "Ungültige Instanz angegeben",
|
||||
"unknown": "Es ist ein unerwarteter Fehler aufgetreten",
|
||||
"empty": "Name darf nicht leer sein",
|
||||
"invalid_characters": "Name darf nur Buchstaben, Zahlen, Leerzeichen, Unterstriche und Bindestriche enthalten",
|
||||
"name_too_long": "Name darf maximal 50 Zeichen lang sein"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Instanzeinstellungen aktualisieren",
|
||||
"description": "Ändern Sie die Einstellungen für diese KI-Assistenteninstanz.",
|
||||
"data": {
|
||||
"model": "KI-Modell",
|
||||
"temperature": "Antwortkreativität (0-2)",
|
||||
"max_tokens": "Maximale Antwortlänge (1-4096)",
|
||||
"request_interval": "Mindestzeitraum zwischen Anfragen (0,1-60 Sekunden)",
|
||||
"context_messages": "Anzahl der vorherigen Nachrichten, die im Kontext enthalten sein sollen (1-20)",
|
||||
"max_history_size": "Maximale Größe des Konversationsverlaufs (1-100)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"ask_question": {
|
||||
"name": "Frage stellen (HA Text AI)",
|
||||
"description": "Senden Sie eine Frage an das KI-Modell und erhalten Sie eine detaillierte Antwort. Die Antwort wird im Konversationsverlauf gespeichert und kann später abgerufen werden.",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instanz",
|
||||
"description": "Name der zu verwendenden HA Text AI-Instanz"
|
||||
},
|
||||
"question": {
|
||||
"name": "Frage",
|
||||
"description": "Ihre Frage oder Aufforderung an den KI-Assistenten"
|
||||
},
|
||||
"context_messages": {
|
||||
"name": "Kontextnachrichten",
|
||||
"description": "Anzahl der vorherigen Nachrichten, die im Kontext enthalten sein sollen (1-20)"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Systemprompt",
|
||||
"description": "Optionaler Systemprompt, um den Kontext für diese spezielle Frage festzulegen"
|
||||
},
|
||||
"model": {
|
||||
"name": "Modell",
|
||||
"description": "Wählen Sie das zu verwendende KI-Modell aus (optional, überschreibt die Standardeinstellung)"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperatur",
|
||||
"description": "Steuert die Antwortkreativität (0.0-2.0)"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max. Token",
|
||||
"description": "Maximale Länge der Antwort (1-4096 Token)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear_history": {
|
||||
"name": "Verlauf löschen",
|
||||
"description": "Alle gespeicherten Fragen und Antworten aus dem Konversationsverlauf löschen",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instanz",
|
||||
"description": "Name der HA Text AI-Instanz, deren Verlauf gelöscht werden soll"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_history": {
|
||||
"name": "Verlauf abrufen",
|
||||
"description": "Konversationsverlauf mit optionalem Filtern und Sortieren abrufen",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instanz",
|
||||
"description": "Name der HA Text AI-Instanz, von der der Verlauf abgerufen werden soll"
|
||||
},
|
||||
"limit": {
|
||||
"name": "Limit",
|
||||
"description": "Anzahl der zurückzugebenden Konversationen (1-100)"
|
||||
},
|
||||
"filter_model": {
|
||||
"name": "Modell filtern",
|
||||
"description": "Konversationen nach einem bestimmten KI-Modell filtern"
|
||||
},
|
||||
"start_date": {
|
||||
"name": "Startdatum",
|
||||
"description": "Konversationen ab diesem Datum/Uhrzeit filtern"
|
||||
},
|
||||
"include_metadata": {
|
||||
"name": "Metadaten einschließen",
|
||||
"description": "Zusätzliche Informationen wie verwendete Token, Antwortzeit usw. einschließen"
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "Sortierreihenfolge",
|
||||
"description": "Sortierreihenfolge für die Ergebnisse (neueste oder älteste zuerst)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_system_prompt": {
|
||||
"name": "Systemprompt festlegen",
|
||||
"description": "Legen Sie Standardanweisungen für das Systemverhalten für alle zukünftigen Konversationen fest",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instanz",
|
||||
"description": "Name der HA Text AI-Instanz, für die der Systemprompt festgelegt werden soll"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "Systemprompt",
|
||||
"description": "Anweisungen, die definieren, wie sich die KI verhalten und antworten soll"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ha_text_ai": {
|
||||
"name": "{name}",
|
||||
"state": {
|
||||
"ready": "Bereit",
|
||||
"processing": "Verarbeitung",
|
||||
"error": "Fehler",
|
||||
"disconnected": "Getrennt",
|
||||
"rate_limited": "Ratenlimit",
|
||||
"maintenance": "Wartung",
|
||||
"initializing": "Initialisierung",
|
||||
"retrying": "Wiederholen",
|
||||
"queued": "Warteschlange"
|
||||
},
|
||||
"state_attributes": {
|
||||
"question": {
|
||||
"name": "Letzte Frage"
|
||||
},
|
||||
"response": {
|
||||
"name": "Letzte Antwort"
|
||||
},
|
||||
"model": {
|
||||
"name": "Aktuelles Modell"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperatur"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max. Token"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Systemprompt"
|
||||
},
|
||||
"response_time": {
|
||||
"name": "Letzte Antwortzeit"
|
||||
},
|
||||
"total_responses": {
|
||||
"name": "Gesamtzahl der Antworten"
|
||||
},
|
||||
"error_count": {
|
||||
"name": "Fehleranzahl"
|
||||
},
|
||||
"last_error": {
|
||||
"name": "Letzter Fehler"
|
||||
},
|
||||
"api_status": {
|
||||
"name": "API-Status"
|
||||
},
|
||||
"tokens_used": {
|
||||
"name": "Verwendete Token insgesamt"
|
||||
},
|
||||
"average_response_time": {
|
||||
"name": "Durchschnittliche Antwortzeit"
|
||||
},
|
||||
"last_request_time": {
|
||||
"name": "Zeitpunkt der letzten Anfrage"
|
||||
},
|
||||
"is_processing": {
|
||||
"name": "Verarbeitungsstatus"
|
||||
},
|
||||
"is_rate_limited": {
|
||||
"name": "Ratenlimit-Status"
|
||||
},
|
||||
"is_maintenance": {
|
||||
"name": "Wartungsstatus"
|
||||
},
|
||||
"api_version": {
|
||||
"name": "API-Version"
|
||||
},
|
||||
"endpoint_status": {
|
||||
"name": "Endpunkt-Status"
|
||||
},
|
||||
"performance_metrics": {
|
||||
"name": "Leistungsmetriken"
|
||||
},
|
||||
"history_size": {
|
||||
"name": "Verlaufsgröße"
|
||||
},
|
||||
"uptime": {
|
||||
"name": "Betriebszeit"
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "Gesamtzahl der Token"
|
||||
},
|
||||
"prompt_tokens": {
|
||||
"name": "Prompt-Token"
|
||||
},
|
||||
"completion_tokens": {
|
||||
"name": "Completion-Token"
|
||||
},
|
||||
"successful_requests": {
|
||||
"name": "Erfolgreiche Anfragen"
|
||||
},
|
||||
"failed_requests": {
|
||||
"name": "Fehlgeschlagene Anfragen"
|
||||
},
|
||||
"average_latency": {
|
||||
"name": "Durchschnittliche Latenz"
|
||||
},
|
||||
"max_latency": {
|
||||
"name": "Maximale Latenz"
|
||||
},
|
||||
"min_latency": {
|
||||
"name": "Minimale Latenz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,260 +1,266 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"provider": {
|
||||
"title": "Select AI Provider",
|
||||
"description": "Choose which AI service provider to use for this instance",
|
||||
"data": {
|
||||
"api_provider": "API Provider",
|
||||
"context_messages": "Number of context messages to retain (1-20)"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "Configure HA Text AI Instance",
|
||||
"description": "Set up a new AI assistant instance with your selected provider",
|
||||
"data": {
|
||||
"name": "Instance name (e.g., 'GPT Assistant', 'Claude Helper')",
|
||||
"api_key": "API key for authentication",
|
||||
"model": "AI model to use",
|
||||
"temperature": "Response creativity (0-2, lower = more focused)",
|
||||
"max_tokens": "Maximum response length (1-4096 tokens)",
|
||||
"api_endpoint": "Custom API endpoint URL (optional)",
|
||||
"request_interval": "Minimum time between requests (0.1-60 seconds)",
|
||||
"context_messages": "Number of context messages to retain (1-20)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"name_exists": "An instance with this name already exists",
|
||||
"invalid_name": "Invalid instance name",
|
||||
"invalid_auth": "Authentication failed - check your API key",
|
||||
"invalid_api_key": "Invalid API key - please verify your credentials",
|
||||
"cannot_connect": "Failed to connect to API service",
|
||||
"invalid_model": "Selected model is not available",
|
||||
"rate_limit": "Rate limit exceeded",
|
||||
"context_length": "Context length exceeded",
|
||||
"rate_limit_exceeded": "API rate limit exceeded",
|
||||
"maintenance": "Service is under maintenance",
|
||||
"invalid_response": "Invalid API response received",
|
||||
"api_error": "API service error occurred",
|
||||
"timeout": "Request timed out",
|
||||
"invalid_instance": "Invalid instance specified",
|
||||
"unknown": "Unexpected error occurred"
|
||||
"config": {
|
||||
"step": {
|
||||
"provider": {
|
||||
"title": "Select AI Provider",
|
||||
"description": "Choose which AI service provider to use for this instance.",
|
||||
"data": {
|
||||
"api_provider": "API Provider",
|
||||
"context_messages": "Number of context messages to retain (1-20)"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "Configure HA Text AI Instance",
|
||||
"description": "Set up a new AI assistant instance with your selected provider.",
|
||||
"data": {
|
||||
"name": "Instance name (e.g., 'GPT Assistant', 'Claude Helper')",
|
||||
"api_key": "API key for authentication",
|
||||
"model": "AI model to use",
|
||||
"temperature": "Response creativity (0-2, lower = more focused)",
|
||||
"max_tokens": "Maximum response length (1-4096 tokens)",
|
||||
"api_endpoint": "Custom API endpoint URL (optional)",
|
||||
"api_provider": "API Provider",
|
||||
"request_interval": "Minimum time between requests (0.1-60 seconds)",
|
||||
"context_messages": "Number of context messages to retain (1-20)",
|
||||
"max_history_size": "Maximum conversation history size (1-100)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Update Instance Settings",
|
||||
"description": "Modify settings for this AI assistant instance",
|
||||
"data": {
|
||||
"model": "AI model",
|
||||
"temperature": "Response creativity (0-2)",
|
||||
"max_tokens": "Maximum response length (1-4096)",
|
||||
"request_interval": "Minimum request interval (0.1-60 seconds)",
|
||||
"context_messages": "Number of previous messages to include in context (1-20)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"ask_question": {
|
||||
"name": "Ask Question (HA Text AI)",
|
||||
"description": "Send a question to the AI model and receive a detailed response. The response will be stored in the conversation history and can be retrieved later.",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "Name of the HA Text AI instance to use"
|
||||
},
|
||||
"question": {
|
||||
"name": "Question",
|
||||
"description": "Your question or prompt for the AI assistant"
|
||||
},
|
||||
"context_messages": {
|
||||
"name": "Context Messages",
|
||||
"description": "Number of previous messages to include in context (1-20)"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "System Prompt",
|
||||
"description": "Optional system prompt to set context for this specific question"
|
||||
},
|
||||
"model": {
|
||||
"name": "Model",
|
||||
"description": "Select AI model to use (optional, overrides default setting)"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperature",
|
||||
"description": "Controls response creativity (0.0-2.0)"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max Tokens",
|
||||
"description": "Maximum length of the response (1-4096 tokens)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear_history": {
|
||||
"name": "Clear History",
|
||||
"description": "Delete all stored questions and responses from the conversation history",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "Name of the HA Text AI instance to clear history for"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_history": {
|
||||
"name": "Get History",
|
||||
"description": "Retrieve conversation history with optional filtering and sorting",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "Name of the HA Text AI instance to get history from"
|
||||
},
|
||||
"limit": {
|
||||
"name": "Limit",
|
||||
"description": "Number of conversations to return (1-100)"
|
||||
},
|
||||
"filter_model": {
|
||||
"name": "Filter Model",
|
||||
"description": "Filter conversations by specific AI model"
|
||||
},
|
||||
"start_date": {
|
||||
"name": "Start Date",
|
||||
"description": "Filter conversations starting from this date/time"
|
||||
},
|
||||
"include_metadata": {
|
||||
"name": "Include Metadata",
|
||||
"description": "Include additional information like tokens used, response time, etc."
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "Sort Order",
|
||||
"description": "Sort order for results (newest or oldest first)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_system_prompt": {
|
||||
"name": "Set System Prompt",
|
||||
"description": "Set default system behavior instructions for all future conversations",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "Name of the HA Text AI instance to set system prompt for"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "System Prompt",
|
||||
"description": "Instructions that define how the AI should behave and respond"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ha_text_ai": {
|
||||
"name": "{name}",
|
||||
"state": {
|
||||
"ready": "Ready",
|
||||
"processing": "Processing",
|
||||
"error": "Error",
|
||||
"disconnected": "Disconnected",
|
||||
"rate_limited": "Rate Limited",
|
||||
"maintenance": "Maintenance",
|
||||
"initializing": "Initializing",
|
||||
"retrying": "Retrying",
|
||||
"queued": "Queued"
|
||||
},
|
||||
"state_attributes": {
|
||||
"question": {
|
||||
"name": "Last Question"
|
||||
},
|
||||
"response": {
|
||||
"name": "Last Response"
|
||||
},
|
||||
"model": {
|
||||
"name": "Current Model"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperature"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max Tokens"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "System Prompt"
|
||||
},
|
||||
"response_time": {
|
||||
"name": "Last Response Time"
|
||||
},
|
||||
"total_responses": {
|
||||
"name": "Total Responses"
|
||||
},
|
||||
"error_count": {
|
||||
"name": "Error Count"
|
||||
},
|
||||
"last_error": {
|
||||
"name": "Last Error"
|
||||
},
|
||||
"api_status": {
|
||||
"name": "API Status"
|
||||
},
|
||||
"tokens_used": {
|
||||
"name": "Total Tokens Used"
|
||||
},
|
||||
"average_response_time": {
|
||||
"name": "Average Response Time"
|
||||
},
|
||||
"last_request_time": {
|
||||
"name": "Last Request Time"
|
||||
},
|
||||
"is_processing": {
|
||||
"name": "Processing Status"
|
||||
},
|
||||
"is_rate_limited": {
|
||||
"name": "Rate Limited Status"
|
||||
},
|
||||
"is_maintenance": {
|
||||
"name": "Maintenance Status"
|
||||
},
|
||||
"api_version": {
|
||||
"name": "API Version"
|
||||
},
|
||||
"endpoint_status": {
|
||||
"name": "Endpoint Status"
|
||||
},
|
||||
"performance_metrics": {
|
||||
"name": "Performance Metrics"
|
||||
},
|
||||
"history_size": {
|
||||
"name": "History Size"
|
||||
},
|
||||
"uptime": {
|
||||
"name": "Uptime"
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "Total Tokens"
|
||||
},
|
||||
"prompt_tokens": {
|
||||
"name": "Prompt Tokens"
|
||||
},
|
||||
"completion_tokens": {
|
||||
"name": "Completion Tokens"
|
||||
},
|
||||
"successful_requests": {
|
||||
"name": "Successful Requests"
|
||||
},
|
||||
"failed_requests": {
|
||||
"name": "Failed Requests"
|
||||
},
|
||||
"average_latency": {
|
||||
"name": "Average Latency"
|
||||
},
|
||||
"max_latency": {
|
||||
"name": "Maximum Latency"
|
||||
},
|
||||
"min_latency": {
|
||||
"name": "Minimum Latency"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"error": {
|
||||
"name_exists": "An instance with this name already exists",
|
||||
"invalid_name": "Invalid instance name",
|
||||
"invalid_auth": "Authentication failed - check your API key",
|
||||
"invalid_api_key": "Invalid API key - please verify your credentials",
|
||||
"cannot_connect": "Failed to connect to API service",
|
||||
"invalid_model": "Selected model is not available",
|
||||
"rate_limit": "Rate limit exceeded",
|
||||
"context_length": "Context length exceeded",
|
||||
"rate_limit_exceeded": "API rate limit exceeded",
|
||||
"maintenance": "Service is under maintenance",
|
||||
"invalid_response": "Invalid API response received",
|
||||
"api_error": "API service error occurred",
|
||||
"timeout": "Request timed out",
|
||||
"invalid_instance": "Invalid instance specified",
|
||||
"unknown": "Unexpected error occurred",
|
||||
"empty": "Name cannot be empty",
|
||||
"invalid_characters": "Name can only contain letters, numbers, spaces, underscores and hyphens",
|
||||
"name_too_long": "Name must be 50 characters or less"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Update Instance Settings",
|
||||
"description": "Modify settings for this AI assistant instance.",
|
||||
"data": {
|
||||
"model": "AI model",
|
||||
"temperature": "Response creativity (0-2)",
|
||||
"max_tokens": "Maximum response length (1-4096)",
|
||||
"request_interval": "Minimum request interval (0.1-60 seconds)",
|
||||
"context_messages": "Number of previous messages to include in context (1-20)",
|
||||
"max_history_size": "Maximum conversation history size (1-100)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"ask_question": {
|
||||
"name": "Ask Question (HA Text AI)",
|
||||
"description": "Send a question to the AI model and receive a detailed response. The response will be stored in the conversation history and can be retrieved later.",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "Name of the HA Text AI instance to use"
|
||||
},
|
||||
"question": {
|
||||
"name": "Question",
|
||||
"description": "Your question or prompt for the AI assistant"
|
||||
},
|
||||
"context_messages": {
|
||||
"name": "Context Messages",
|
||||
"description": "Number of previous messages to include in context (1-20)"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "System Prompt",
|
||||
"description": "Optional system prompt to set context for this specific question"
|
||||
},
|
||||
"model": {
|
||||
"name": "Model",
|
||||
"description": "Select AI model to use (optional, overrides default setting)"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperature",
|
||||
"description": "Controls response creativity (0.0-2.0)"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max Tokens",
|
||||
"description": "Maximum length of the response (1-4096 tokens)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear_history": {
|
||||
"name": "Clear History",
|
||||
"description": "Delete all stored questions and responses from the conversation history",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "Name of the HA Text AI instance to clear history for"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_history": {
|
||||
"name": "Get History",
|
||||
"description": "Retrieve conversation history with optional filtering and sorting",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "Name of the HA Text AI instance to get history from"
|
||||
},
|
||||
"limit": {
|
||||
"name": "Limit",
|
||||
"description": "Number of conversations to return (1-100)"
|
||||
},
|
||||
"filter_model": {
|
||||
"name": "Filter Model",
|
||||
"description": "Filter conversations by specific AI model"
|
||||
},
|
||||
"start_date": {
|
||||
"name": "Start Date",
|
||||
"description": "Filter conversations starting from this date/time"
|
||||
},
|
||||
"include_metadata": {
|
||||
"name": "Include Metadata",
|
||||
"description": "Include additional information like tokens used, response time, etc."
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "Sort Order",
|
||||
"description": "Sort order for results (newest or oldest first)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_system_prompt": {
|
||||
"name": "Set System Prompt",
|
||||
"description": "Set default system behavior instructions for all future conversations",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "Name of the HA Text AI instance to set system prompt for"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "System Prompt",
|
||||
"description": "Instructions that define how the AI should behave and respond"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ha_text_ai": {
|
||||
"name": "{name}",
|
||||
"state": {
|
||||
"ready": "Ready",
|
||||
"processing": "Processing",
|
||||
"error": "Error",
|
||||
"disconnected": "Disconnected",
|
||||
"rate_limited": "Rate Limited",
|
||||
"maintenance": "Maintenance",
|
||||
"initializing": "Initializing",
|
||||
"retrying": "Retrying",
|
||||
"queued": "Queued"
|
||||
},
|
||||
"state_attributes": {
|
||||
"question": {
|
||||
"name": "Last Question"
|
||||
},
|
||||
"response": {
|
||||
"name": "Last Response"
|
||||
},
|
||||
"model": {
|
||||
"name": "Current Model"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperature"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max Tokens"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "System Prompt"
|
||||
},
|
||||
"response_time": {
|
||||
"name": "Last Response Time"
|
||||
},
|
||||
"total_responses": {
|
||||
"name": "Total Responses"
|
||||
},
|
||||
"error_count": {
|
||||
"name": "Error Count"
|
||||
},
|
||||
"last_error": {
|
||||
"name": "Last Error"
|
||||
},
|
||||
"api_status": {
|
||||
"name": "API Status"
|
||||
},
|
||||
"tokens_used": {
|
||||
"name": "Total Tokens Used"
|
||||
},
|
||||
"average_response_time": {
|
||||
"name": "Average Response Time"
|
||||
},
|
||||
"last_request_time": {
|
||||
"name": "Last Request Time"
|
||||
},
|
||||
"is_processing": {
|
||||
"name": "Processing Status"
|
||||
},
|
||||
"is_rate_limited": {
|
||||
"name": "Rate Limited Status"
|
||||
},
|
||||
"is_maintenance": {
|
||||
"name": "Maintenance Status"
|
||||
},
|
||||
"api_version": {
|
||||
"name": "API Version"
|
||||
},
|
||||
"endpoint_status": {
|
||||
"name": "Endpoint Status"
|
||||
},
|
||||
"performance_metrics": {
|
||||
"name": "Performance Metrics"
|
||||
},
|
||||
"history_size": {
|
||||
"name": "History Size"
|
||||
},
|
||||
"uptime": {
|
||||
"name": "Uptime"
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "Total Tokens"
|
||||
},
|
||||
"prompt_tokens": {
|
||||
"name": "Prompt Tokens"
|
||||
},
|
||||
"completion_tokens": {
|
||||
"name": "Completion Tokens"
|
||||
},
|
||||
"successful_requests": {
|
||||
"name": "Successful Requests"
|
||||
},
|
||||
"failed_requests": {
|
||||
"name": "Failed Requests"
|
||||
},
|
||||
"average_latency": {
|
||||
"name": "Average Latency"
|
||||
},
|
||||
"max_latency": {
|
||||
"name": "Maximum Latency"
|
||||
},
|
||||
"min_latency": {
|
||||
"name": "Minimum Latency"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,260 +1,266 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"provider": {
|
||||
"title": "Выбор провайдера ИИ",
|
||||
"description": "Выберите сервис ИИ для этого экземпляра",
|
||||
"data": {
|
||||
"api_provider": "Провайдер API",
|
||||
"context_messages": "Количество сообщений в контексте (1-20)"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "Настройка экземпляра HA Text AI",
|
||||
"description": "Настройте нового помощника ИИ с выбранным провайдером",
|
||||
"data": {
|
||||
"name": "Название экземпляра (например, 'GPT Помощник', 'Claude Ассистент')",
|
||||
"api_key": "API-ключ для аутентификации",
|
||||
"model": "Модель ИИ для использования",
|
||||
"temperature": "Креативность ответов (0-2, меньше = более сфокусированно)",
|
||||
"max_tokens": "Максимальная длина ответа (1-4096 токенов)",
|
||||
"api_endpoint": "Пользовательский URL-адрес API (необязательно)",
|
||||
"request_interval": "Минимальный интервал между запросами (0.1-60 секунд)",
|
||||
"context_messages": "Количество сохраняемых контекстных сообщений (1-20)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"name_exists": "Экземпляр с таким именем уже существует",
|
||||
"invalid_name": "Некорректное имя экземпляра",
|
||||
"invalid_auth": "Ошибка аутентификации - проверьте API-ключ",
|
||||
"invalid_api_key": "Неверный API-ключ - проверьте учетные данные",
|
||||
"cannot_connect": "Не удалось подключиться к сервису API",
|
||||
"invalid_model": "Выбранная модель недоступна",
|
||||
"rate_limit": "Превышен лимит запросов",
|
||||
"context_length": "Превышена длина контекста",
|
||||
"rate_limit_exceeded": "Превышен лимит API",
|
||||
"maintenance": "Сервис на техническом обслуживании",
|
||||
"invalid_response": "Получен некорректный ответ API",
|
||||
"api_error": "Произошла ошибка сервиса API",
|
||||
"timeout": "Время ожидания истекло",
|
||||
"invalid_instance": "Указан неверный экземпляр",
|
||||
"unknown": "Произошла непредвиденная ошибка"
|
||||
"config": {
|
||||
"step": {
|
||||
"provider": {
|
||||
"title": "Выбор поставщика ИИ",
|
||||
"description": "Выберите поставщика услуг ИИ для этой инстанции.",
|
||||
"data": {
|
||||
"api_provider": "Поставщик API",
|
||||
"context_messages": "Количество контекстных сообщений для сохранения (1-20)"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "Настройка инстанции HA Text AI",
|
||||
"description": "Настройте новую инстанцию помощника ИИ с выбранным вами поставщиком.",
|
||||
"data": {
|
||||
"name": "Имя инстанции (например, 'GPT Assistant', 'Claude Helper')",
|
||||
"api_key": "API-ключ для аутентификации",
|
||||
"model": "Используемая модель ИИ",
|
||||
"temperature": "Креативность ответа (0-2, чем ниже, тем больше фокусировки)",
|
||||
"max_tokens": "Максимальная длина ответа (1-4096 токенов)",
|
||||
"api_endpoint": "Пользовательский URL-адрес конечной точки API (необязательно)",
|
||||
"api_provider": "Поставщик API",
|
||||
"request_interval": "Минимальное время между запросами (0,1-60 секунд)",
|
||||
"context_messages": "Количество контекстных сообщений для сохранения (1-20)",
|
||||
"max_history_size": "Максимальный размер истории разговора (1-100)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Обновление настроек экземпляра",
|
||||
"description": "Измените настройки для этого помощника ИИ",
|
||||
"data": {
|
||||
"model": "Модель ИИ",
|
||||
"temperature": "Креативность ответов (0-2)",
|
||||
"max_tokens": "Максимальная длина ответа (1-4096)",
|
||||
"request_interval": "Минимальный интервал запросов (0.1-60 секунд)",
|
||||
"context_messages": "Количество предыдущих сообщений для включения в контекст (1-20)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"ask_question": {
|
||||
"name": "Задать вопрос (HA Text AI)",
|
||||
"description": "Отправьте вопрос модели ИИ и получите подробный ответ. Ответ будет сохранен в истории беседы и может быть извлечен позже.",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Экземпляр",
|
||||
"description": "Название экземпляра HA Text AI для использования"
|
||||
},
|
||||
"question": {
|
||||
"name": "Вопрос",
|
||||
"description": "Ваш вопрос или запрос помощнику ИИ"
|
||||
},
|
||||
"context_messages": {
|
||||
"name": "Контекстные сообщения",
|
||||
"description": "Количество предыдущих сообщений для включения в контекст (1-20)"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Системный промпт",
|
||||
"description": "Необязательный системный промпт для установки контекста для этого конкретного вопроса"
|
||||
},
|
||||
"model": {
|
||||
"name": "Модель",
|
||||
"description": "Выберите модель ИИ (необязательно, переопределяет настройки по умолчанию)"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Температура",
|
||||
"description": "Управляет креативностью ответа (0.0-2.0)"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Максимальное количество токенов",
|
||||
"description": "Максимальная длина ответа (1-4096 токенов)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear_history": {
|
||||
"name": "Очистить историю",
|
||||
"description": "Удалить все сохраненные вопросы и ответы из истории беседы",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Экземпляр",
|
||||
"description": "Название экземпляра HA Text AI для очистки истории"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_history": {
|
||||
"name": "Получить историю",
|
||||
"description": "Получить историю беседы с дополнительной фильтрацией и сортировкой",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Экземпляр",
|
||||
"description": "Название экземпляра HA Text AI для получения истории"
|
||||
},
|
||||
"limit": {
|
||||
"name": "Лимит",
|
||||
"description": "Количество возвращаемых бесед (1-100)"
|
||||
},
|
||||
"filter_model": {
|
||||
"name": "Фильтр модели",
|
||||
"description": "Фильтрация бесед по конкретной модели ИИ"
|
||||
},
|
||||
"start_date": {
|
||||
"name": "Начальная дата",
|
||||
"description": "Фильтрация бесед, начиная с указанной даты/времени"
|
||||
},
|
||||
"include_metadata": {
|
||||
"name": "Включить метаданные",
|
||||
"description": "Включить дополнительную информацию, например, использованные токены, время ответа и т.д."
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "Порядок сортировки",
|
||||
"description": "Порядок результатов (сначала новые или старые)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_system_prompt": {
|
||||
"name": "Установить системный промпт",
|
||||
"description": "Установить инструкции по умолчанию для поведения ИИ во всех будущих разговорах",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Экземпляр",
|
||||
"description": "Название экземпляра HA Text AI для установки системного промпта"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "Системный промпт",
|
||||
"description": "Инструкции, определяющие, как ИИ должен вести себя и отвечать"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ha_text_ai": {
|
||||
"name": "{name}",
|
||||
"state": {
|
||||
"ready": "Готов",
|
||||
"processing": "Обработка",
|
||||
"error": "Ошибка",
|
||||
"disconnected": "Отключен",
|
||||
"rate_limited": "Лимит запросов",
|
||||
"maintenance": "Техническое обслуживание",
|
||||
"initializing": "Инициализация",
|
||||
"retrying": "Повторная попытка",
|
||||
"queued": "В очереди"
|
||||
},
|
||||
"state_attributes": {
|
||||
"question": {
|
||||
"name": "Последний вопрос"
|
||||
},
|
||||
"response": {
|
||||
"name": "Последний ответ"
|
||||
},
|
||||
"model": {
|
||||
"name": "Текущая модель"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Температура"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Максимальное количество токенов"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Системный промпт"
|
||||
},
|
||||
"response_time": {
|
||||
"name": "Время последнего ответа"
|
||||
},
|
||||
"total_responses": {
|
||||
"name": "Всего ответов"
|
||||
},
|
||||
"error_count": {
|
||||
"name": "Количество ошибок"
|
||||
},
|
||||
"last_error": {
|
||||
"name": "Последняя ошибка"
|
||||
},
|
||||
"api_status": {
|
||||
"name": "Статус API"
|
||||
},
|
||||
"tokens_used": {
|
||||
"name": "Всего использовано токенов"
|
||||
},
|
||||
"average_response_time": {
|
||||
"name": "Среднее время ответа"
|
||||
},
|
||||
"last_request_time": {
|
||||
"name": "Время последнего запроса"
|
||||
},
|
||||
"is_processing": {
|
||||
"name": "Статус обработки"
|
||||
},
|
||||
"is_rate_limited": {
|
||||
"name": "Статус лимита запросов"
|
||||
},
|
||||
"is_maintenance": {
|
||||
"name": "Статус обслуживания"
|
||||
},
|
||||
"api_version": {
|
||||
"name": "Версия API"
|
||||
},
|
||||
"endpoint_status": {
|
||||
"name": "Статус эндпоинта"
|
||||
},
|
||||
"performance_metrics": {
|
||||
"name": "Показатели производительности"
|
||||
},
|
||||
"history_size": {
|
||||
"name": "Размер истории"
|
||||
},
|
||||
"uptime": {
|
||||
"name": "Время работы"
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "Всего токенов"
|
||||
},
|
||||
"prompt_tokens": {
|
||||
"name": "Токены промпта"
|
||||
},
|
||||
"completion_tokens": {
|
||||
"name": "Токены завершения"
|
||||
},
|
||||
"successful_requests": {
|
||||
"name": "Успешные запросы"
|
||||
},
|
||||
"failed_requests": {
|
||||
"name": "Неудачные запросы"
|
||||
},
|
||||
"average_latency": {
|
||||
"name": "Средняя задержка"
|
||||
},
|
||||
"max_latency": {
|
||||
"name": "Максимальная задержка"
|
||||
},
|
||||
"min_latency": {
|
||||
"name": "Минимальная задержка"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"error": {
|
||||
"name_exists": "Инстанция с таким именем уже существует",
|
||||
"invalid_name": "Некорректное имя инстанции",
|
||||
"invalid_auth": "Аутентификация не удалась - проверьте ваш API-ключ",
|
||||
"invalid_api_key": "Неверный API-ключ - проверьте ваши учетные данные",
|
||||
"cannot_connect": "Не удалось подключиться к службе API",
|
||||
"invalid_model": "Выбранная модель недоступна",
|
||||
"rate_limit": "Превышен лимит запросов",
|
||||
"context_length": "Превышена длина контекста",
|
||||
"rate_limit_exceeded": "Превышен лимит запросов API",
|
||||
"maintenance": "Сервис находится на техническом обслуживании",
|
||||
"invalid_response": "Получен неверный ответ API",
|
||||
"api_error": "Произошла ошибка службы API",
|
||||
"timeout": "Запрос превысил время ожидания",
|
||||
"invalid_instance": "Указана неверная инстанция",
|
||||
"unknown": "Произошла непредвиденная ошибка",
|
||||
"empty": "Имя не может быть пустым",
|
||||
"invalid_characters": "Имя может содержать только буквы, цифры, пробелы, подчеркивания и дефисы",
|
||||
"name_too_long": "Имя должно быть не более 50 символов"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Обновление настроек инстанции",
|
||||
"description": "Измените настройки для этой инстанции помощника ИИ.",
|
||||
"data": {
|
||||
"model": "Модель ИИ",
|
||||
"temperature": "Креативность ответа (0-2)",
|
||||
"max_tokens": "Максимальная длина ответа (1-4096)",
|
||||
"request_interval": "Минимальный интервал между запросами (0,1-60 секунд)",
|
||||
"context_messages": "Количество предыдущих сообщений для включения в контекст (1-20)",
|
||||
"max_history_size": "Максимальный размер истории разговора (1-100)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"ask_question": {
|
||||
"name": "Задать вопрос (HA Text AI)",
|
||||
"description": "Отправьте вопрос модели ИИ и получите подробный ответ. Ответ будет сохранен в истории разговора и может быть извлечен позже.",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Инстанция",
|
||||
"description": "Имя используемой инстанции HA Text AI"
|
||||
},
|
||||
"question": {
|
||||
"name": "Вопрос",
|
||||
"description": "Ваш вопрос или запрос к помощнику ИИ"
|
||||
},
|
||||
"context_messages": {
|
||||
"name": "Контекстные сообщения",
|
||||
"description": "Количество предыдущих сообщений для включения в контекст (1-20)"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Системный запрос",
|
||||
"description": "Необязательный системный запрос для установки контекста для этого конкретного вопроса"
|
||||
},
|
||||
"model": {
|
||||
"name": "Модель",
|
||||
"description": "Выберите модель ИИ для использования (необязательно, переопределяет настройку по умолчанию)"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Температура",
|
||||
"description": "Управляет креативностью ответа (0.0-2.0)"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Макс. токенов",
|
||||
"description": "Максимальная длина ответа (1-4096 токенов)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear_history": {
|
||||
"name": "Очистить историю",
|
||||
"description": "Удалить все сохраненные вопросы и ответы из истории разговора",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Инстанция",
|
||||
"description": "Имя инстанции HA Text AI, для которой нужно очистить историю"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_history": {
|
||||
"name": "Получить историю",
|
||||
"description": "Извлечь историю разговора с возможностью фильтрации и сортировки",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Инстанция",
|
||||
"description": "Имя инстанции HA Text AI, из которой нужно получить историю"
|
||||
},
|
||||
"limit": {
|
||||
"name": "Лимит",
|
||||
"description": "Количество разговоров для возврата (1-100)"
|
||||
},
|
||||
"filter_model": {
|
||||
"name": "Фильтр модели",
|
||||
"description": "Фильтрация разговоров по определенной модели ИИ"
|
||||
},
|
||||
"start_date": {
|
||||
"name": "Дата начала",
|
||||
"description": "Фильтрация разговоров, начиная с этой даты/времени"
|
||||
},
|
||||
"include_metadata": {
|
||||
"name": "Включить метаданные",
|
||||
"description": "Включить дополнительную информацию, такую как использованные токены, время ответа и т.д."
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "Порядок сортировки",
|
||||
"description": "Порядок сортировки результатов (самые новые или самые старые)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_system_prompt": {
|
||||
"name": "Установить системный запрос",
|
||||
"description": "Установить инструкции по умолчанию для поведения системы для всех будущих разговоров",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Инстанция",
|
||||
"description": "Имя инстанции HA Text AI, для которой нужно установить системный запрос"
|
||||
},
|
||||
"prompt": {
|
||||
"name": "Системный запрос",
|
||||
"description": "Инструкции, определяющие, как ИИ должен себя вести и отвечать"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ha_text_ai": {
|
||||
"name": "{name}",
|
||||
"state": {
|
||||
"ready": "Готов",
|
||||
"processing": "Обработка",
|
||||
"error": "Ошибка",
|
||||
"disconnected": "Отключен",
|
||||
"rate_limited": "Лимит запросов",
|
||||
"maintenance": "Техническое обслуживание",
|
||||
"initializing": "Инициализация",
|
||||
"retrying": "Повторная попытка",
|
||||
"queued": "В очереди"
|
||||
},
|
||||
"state_attributes": {
|
||||
"question": {
|
||||
"name": "Последний вопрос"
|
||||
},
|
||||
"response": {
|
||||
"name": "Последний ответ"
|
||||
},
|
||||
"model": {
|
||||
"name": "Текущая модель"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Температура"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Макс. токенов"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Системный запрос"
|
||||
},
|
||||
"response_time": {
|
||||
"name": "Время последнего ответа"
|
||||
},
|
||||
"total_responses": {
|
||||
"name": "Всего ответов"
|
||||
},
|
||||
"error_count": {
|
||||
"name": "Количество ошибок"
|
||||
},
|
||||
"last_error": {
|
||||
"name": "Последняя ошибка"
|
||||
},
|
||||
"api_status": {
|
||||
"name": "Статус API"
|
||||
},
|
||||
"tokens_used": {
|
||||
"name": "Использовано токенов всего"
|
||||
},
|
||||
"average_response_time": {
|
||||
"name": "Среднее время ответа"
|
||||
},
|
||||
"last_request_time": {
|
||||
"name": "Время последнего запроса"
|
||||
},
|
||||
"is_processing": {
|
||||
"name": "Статус обработки"
|
||||
},
|
||||
"is_rate_limited": {
|
||||
"name": "Статус ограничения запросов"
|
||||
},
|
||||
"is_maintenance": {
|
||||
"name": "Статус технического обслуживания"
|
||||
},
|
||||
"api_version": {
|
||||
"name": "Версия API"
|
||||
},
|
||||
"endpoint_status": {
|
||||
"name": "Статус конечной точки"
|
||||
},
|
||||
"performance_metrics": {
|
||||
"name": "Метрики производительности"
|
||||
},
|
||||
"history_size": {
|
||||
"name": "Размер истории"
|
||||
},
|
||||
"uptime": {
|
||||
"name": "Время работы"
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "Всего токенов"
|
||||
},
|
||||
"prompt_tokens": {
|
||||
"name": "Токены запроса"
|
||||
},
|
||||
"completion_tokens": {
|
||||
"name": "Токены завершения"
|
||||
},
|
||||
"successful_requests": {
|
||||
"name": "Успешных запросов"
|
||||
},
|
||||
"failed_requests": {
|
||||
"name": "Неудачных запросов"
|
||||
},
|
||||
"average_latency": {
|
||||
"name": "Средняя задержка"
|
||||
},
|
||||
"max_latency": {
|
||||
"name": "Максимальная задержка"
|
||||
},
|
||||
"min_latency": {
|
||||
"name": "Минимальная задержка"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{
|
||||
"name": "HA text AI",
|
||||
"render_readme": true,
|
||||
"icon": "mdi:robot",
|
||||
"domains": ["sensor"],
|
||||
"homeassistant": "2024.11.0",
|
||||
"version": "2.0.0",
|
||||
"documentation": "https://github.com/smkrv/ha-text-ai"
|
||||
"homeassistant": "2024.11.0"
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 923 KiB |
+20
-14
@@ -2,18 +2,24 @@
|
||||
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
|
||||
│
|
||||
└── strings/
|
||||
├── en.json
|
||||
├── de.json
|
||||
└── ru.json
|
||||
├── 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
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user