-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🚀 RELEASE: Version bump to 1.2.0. Added Home Assistant Conversation I…
…ntegration. #3
Showing
21 changed files
with
878 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,13 @@ | ||
# meeseeks-api | ||
# Meeseeks API Server | ||
<p align="center"> | ||
<a href="https://github.com/bearlike/Personal-Assistant/wiki"><img alt="Wiki" src="https://img.shields.io/badge/GitHub-Wiki-blue?style=for-the-badge&logo=github"></a> | ||
<a href="https://github.com/bearlike/Personal-Assistant/pkgs/container/meeseeks-chat"><img src="https://img.shields.io/badge/ghcr.io-bearlike/meeseeks−api:latest-blue?style=for-the-badge&logo=docker&logoColor=white" alt="Docker Image"></a> | ||
<a href="https://github.com/bearlike/Personal-Assistant/releases"><img src="https://img.shields.io/github/v/release/bearlike/Personal-Assistant?style=for-the-badge&" alt="GitHub Release"></a> | ||
</p> | ||
|
||
- REST API Engine wrapped around the meeseeks-core. | ||
- No components are explicitly tested for safety or security. Use with caution in a production environment. | ||
- For more information, such as installation, please check out the [Wiki](https://github.com/bearlike/Personal-Assistant/wiki). | ||
|
||
[Link to GitHub Repository](https://github.com/bearlike/Personal-Assistant/edit/main/README.md) | ||
|
||
[Link to GitHub Repository](https://github.com/bearlike/Personal-Assistant) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,18 @@ | ||
# meeseeks-chat | ||
# Meeseeks - Chat Interface | ||
<p align="center"> | ||
<a href="https://github.com/bearlike/Personal-Assistant/wiki"><img alt="Wiki" src="https://img.shields.io/badge/GitHub-Wiki-blue?style=for-the-badge&logo=github"></a> | ||
<a href="https://github.com/bearlike/Personal-Assistant/pkgs/container/meeseeks-chat"><img src="https://img.shields.io/badge/ghcr.io-bearlike/meeseeks−chat:latest-blue?style=for-the-badge&logo=docker&logoColor=white" alt="Docker Image"></a> | ||
<a href="https://github.com/bearlike/Personal-Assistant/releases"><img src="https://img.shields.io/github/v/release/bearlike/Personal-Assistant?style=for-the-badge&" alt="GitHub Release"></a> | ||
</p> | ||
|
||
Chat Interface wrapped around the meeseeks-core. Powered by Streamlit. | ||
|
||
[Link to GitHub](https://github.com/bearlike/Personal-Assistant/edit/main/README.md) | ||
<p align="center"> | ||
<img src="../docs/screenshot_chat_app_1.png" alt="Screenshot of Meeseks WebUI" height="512px"> | ||
</p> | ||
|
||
|
||
- Chat Interface wrapped around the meeseeks-core. Powered by Streamlit. | ||
- For more information, such as installation, please check out the [Wiki](https://github.com/bearlike/Personal-Assistant/wiki). | ||
|
||
|
||
[Link to GitHub](https://github.com/bearlike/Personal-Assistant) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Home Assistant Conversation Integration for Meeseeks 🚀 | ||
|
||
<p align="center"> | ||
<a href="https://github.com/bearlike/Personal-Assistant/wiki"><img alt="Wiki" src="https://img.shields.io/badge/GitHub-Wiki-blue?style=for-the-badge&logo=github"></a> | ||
<a href="https://github.com/bearlike/Personal-Assistant/releases"><img src="https://img.shields.io/github/v/release/bearlike/Personal-Assistant?style=for-the-badge&" alt="GitHub Release"></a> | ||
</p> | ||
|
||
|
||
<table align="center"> | ||
<tr> | ||
<th>Answer questions and interpret sensor information</th> | ||
<th>Control devices and entities</th> | ||
</tr> | ||
<tr> | ||
<td align="center"><img src="../docs/screenshot_ha_assist_1.png" alt="Screenshot" height="512px"></td> | ||
<td align="center"><img src="../docs/screenshot_ha_assist_2.png" alt="Screenshot" height="512px"></td> | ||
</tr> | ||
</table> | ||
|
||
- Home Assistant Conversation Integration for Meeseeks. Can be used with HA Assist ⭐. | ||
- Wrapped around the REST API Engine for Meeseeks. 100% coverage of Meeseeks API. | ||
- No components are explicitly tested for safety or security. Use with caution in a production environment. | ||
- For more information, such as installation, please check out the [Wiki](https://github.com/bearlike/Personal-Assistant/wiki). | ||
|
||
[Link to GitHub Repository](https://github.com/bearlike/Personal-Assistant) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
"""Custom integration to integrate meeseeks_conversation with Home Assistant. | ||
For more details about this integration, please refer to | ||
https://github.com/bearlike/personal-Assistant/ | ||
""" | ||
from __future__ import annotations | ||
|
||
from typing import Literal | ||
|
||
from homeassistant.components import conversation | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import MATCH_ALL | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError | ||
from homeassistant.helpers import intent, template | ||
from homeassistant.helpers.aiohttp_client import async_get_clientsession | ||
from homeassistant.util import ulid | ||
|
||
from .api import MeeseeksApiClient | ||
from .const import ( | ||
DOMAIN, LOGGER, | ||
CONF_BASE_URL, | ||
CONF_TIMEOUT, | ||
DEFAULT_TIMEOUT, | ||
) | ||
# User-defined imports | ||
from .coordinator import MeeseeksDataUpdateCoordinator | ||
from .exceptions import ( | ||
ApiClientError, | ||
ApiCommError, | ||
ApiJsonError, | ||
ApiTimeoutError | ||
) | ||
# from .helpers import get_exposed_entities | ||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Set up Meeseeks conversation using UI.""" | ||
# https://developers.home-assistant.io/docs/config_entries_index/#setting-up-an-entry | ||
hass.data.setdefault(DOMAIN, {}) | ||
client = MeeseeksApiClient( | ||
base_url=entry.data[CONF_BASE_URL], | ||
timeout=entry.options.get(CONF_TIMEOUT, DEFAULT_TIMEOUT), | ||
session=async_get_clientsession(hass), | ||
) | ||
|
||
hass.data[DOMAIN][entry.entry_id] = coordinator = MeeseeksDataUpdateCoordinator( | ||
hass, | ||
client, | ||
) | ||
# https://developers.home-assistant.io/docs/integration_fetching_data#coordinated-single-api-poll-for-data-for-all-entities | ||
await coordinator.async_config_entry_first_refresh() | ||
|
||
try: | ||
# TODO: Heartbeat check is not implemented but it is still wrapped. | ||
response = await client.async_get_heartbeat() | ||
if not response: | ||
raise ApiClientError("Invalid Meeseeks server") | ||
except ApiClientError as err: | ||
raise ConfigEntryNotReady(err) from err | ||
|
||
entry.async_on_unload(entry.add_update_listener(async_reload_entry)) | ||
|
||
conversation.async_set_agent( | ||
hass, entry, MeeseeksAgent(hass, entry, client)) | ||
return True | ||
|
||
|
||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Unload Meeseeks conversation.""" | ||
conversation.async_unset_agent(hass, entry) | ||
return True | ||
|
||
|
||
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: | ||
"""Reload Meeseeks conversation.""" | ||
await async_unload_entry(hass, entry) | ||
await async_setup_entry(hass, entry) | ||
|
||
|
||
class MeeseeksAgent(conversation.AbstractConversationAgent): | ||
"""Meeseeks conversation agent.""" | ||
|
||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry, client: MeeseeksApiClient) -> None: | ||
"""Initialize the agent.""" | ||
self.hass = hass | ||
self.entry = entry | ||
self.client = client | ||
self.history: dict[str, dict] = {} | ||
|
||
@property | ||
def supported_languages(self) -> list[str] | Literal["*"]: | ||
"""Return a list of supported languages.""" | ||
return MATCH_ALL | ||
|
||
async def async_process( | ||
self, user_input: conversation.ConversationInput | ||
) -> conversation.ConversationResult: | ||
"""Process a sentence.""" | ||
# * If needeed in the future, uncomment the following lines | ||
# raw_system_prompt = self.entry.options.get( | ||
# CONF_PROMPT_SYSTEM, DEFAULT_PROMPT_SYSTEM) | ||
# exposed_entities = get_exposed_entities(self.hass) | ||
# ! Currently, history is not used but still implemented for future use | ||
if user_input.conversation_id in self.history: | ||
conversation_id = user_input.conversation_id | ||
messages = self.history[conversation_id] | ||
else: | ||
conversation_id = ulid.ulid() | ||
system_prompt = "" | ||
messages = { | ||
"system": system_prompt, | ||
"context": None, | ||
} | ||
|
||
messages["prompt"] = user_input.text | ||
|
||
try: | ||
response = await self.query(messages) | ||
except HomeAssistantError as err: | ||
LOGGER.error("Something went wrong: %s", err) | ||
intent_response = intent.IntentResponse( | ||
language=user_input.language) | ||
intent_response.async_set_error( | ||
intent.IntentResponseErrorCode.UNKNOWN, | ||
"Something went wrong, please check the logs for more information.", | ||
) | ||
return conversation.ConversationResult( | ||
response=intent_response, conversation_id=conversation_id | ||
) | ||
|
||
messages["context"] = response["context"] | ||
self.history[conversation_id] = messages | ||
|
||
intent_response = intent.IntentResponse(language=user_input.language) | ||
intent_response.async_set_speech(response["response"]) | ||
return conversation.ConversationResult( | ||
response=intent_response, conversation_id=conversation_id | ||
) | ||
|
||
def _async_generate_prompt(self, raw_prompt: str, exposed_entities) -> str: | ||
"""Generate a prompt for the user.""" | ||
return template.Template(raw_prompt, self.hass).async_render( | ||
{ | ||
"ha_name": self.hass.config.location_name, | ||
"exposed_entities": exposed_entities, | ||
}, | ||
parse_result=False, | ||
) | ||
|
||
async def query( | ||
self, | ||
messages | ||
): | ||
"""Process a sentence.""" | ||
# model = self.entry.options.get(CONF_MODEL, DEFAULT_MODEL) | ||
# LOGGER.debug("Prompt for %s: %s", model, messages["prompt"]) | ||
|
||
# TODO: $context, and $system are not used but still implemented for | ||
# future use | ||
# * Generator | ||
result = await self.client.async_generate({ | ||
"context": messages["context"], | ||
"system": messages["system"], | ||
"prompt": messages["prompt"], | ||
}) | ||
response: str = result["task_result"] | ||
LOGGER.debug("Response %s", response) | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
""" Meeseeks API Client. """ | ||
from __future__ import annotations | ||
|
||
import aiohttp | ||
import async_timeout | ||
import json | ||
|
||
# User-defined imports | ||
from .exceptions import ( | ||
ApiClientError, | ||
ApiCommError, | ||
ApiJsonError, | ||
ApiTimeoutError | ||
) | ||
from .const import LOGGER | ||
|
||
|
||
class MeeseeksApiClient: | ||
"""Meeseeks API Client.""" | ||
|
||
def __init__( | ||
self, | ||
base_url: str, | ||
timeout: int, | ||
session: aiohttp.ClientSession, | ||
) -> None: | ||
"""Sample API Client.""" | ||
self._base_url = base_url.rstrip("/") | ||
self._api_key = 'msk-strong-password' | ||
self.timeout = timeout | ||
self._session = session | ||
|
||
async def async_get_heartbeat(self) -> bool: | ||
"""Get heartbeat from the API.""" | ||
# TODO: Implement a heartbeat check | ||
return True | ||
|
||
async def async_get_models(self) -> any: | ||
"""Get models from the API.""" | ||
# TODO: This is monkey-patched for now | ||
response_data = { | ||
"models": [ | ||
{ | ||
"name": "meeseeks", | ||
"modified_at": "2023-11-01T00:00:00.000000000-04:00", | ||
"size": 0, | ||
"digest": None | ||
} | ||
] | ||
} | ||
return json.dumps(response_data) | ||
|
||
async def async_generate(self, data: dict | None = None,) -> any: | ||
"""Generate a completion from the API.""" | ||
url_query = f"{self._base_url}/api/query" | ||
data_custom = { | ||
'query': str(data["prompt"]).strip(), | ||
} | ||
# Pass headers as None to use the default headers | ||
return await self._meeseeks_api_wrapper( | ||
method="post", | ||
url=url_query, | ||
data=data_custom, | ||
headers=None, | ||
) | ||
|
||
async def _meeseeks_api_wrapper( | ||
self, | ||
method: str, | ||
url: str, | ||
data: dict | None = None, | ||
headers: dict | None = None, | ||
decode_json: bool = True, | ||
) -> any: | ||
"""Get information from the API.""" | ||
if headers is None: | ||
headers = { | ||
'accept': 'application/json', | ||
'X-API-KEY': self._api_key, | ||
'Content-Type': 'application/json', | ||
} | ||
async with async_timeout.timeout(self.timeout): | ||
response = await self._session.request( | ||
method=method, | ||
url=url, | ||
headers=headers, | ||
json=data, | ||
) | ||
response.raise_for_status() | ||
|
||
if decode_json: | ||
response_data = await response.json() | ||
if response.status == 404: | ||
raise ApiJsonError(response_data["error"]) | ||
LOGGER.debug(f"Response data: {response_data}") | ||
response_data["response"] = response_data["task_result"] | ||
response_data["context"] = response_data["task_result"] | ||
return response_data | ||
else: | ||
LOGGER.debug("Fallback to text response") | ||
return await response.text() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
"""Adds config flow for Meeseeks.""" | ||
from __future__ import annotations | ||
|
||
import types | ||
from typing import Any | ||
import voluptuous as vol | ||
|
||
from homeassistant import config_entries | ||
from homeassistant.data_entry_flow import FlowResult | ||
from homeassistant.helpers import config_validation as cv | ||
from homeassistant.helpers.aiohttp_client import async_create_clientsession | ||
from homeassistant.helpers.selector import ( | ||
NumberSelector, | ||
NumberSelectorConfig, | ||
TemplateSelector, | ||
SelectSelector, | ||
SelectSelectorConfig, | ||
SelectSelectorMode, | ||
SelectOptionDict | ||
) | ||
# User-defined imports | ||
from .api import MeeseeksApiClient | ||
from .const import ( | ||
DOMAIN, LOGGER, | ||
MENU_OPTIONS, | ||
|
||
CONF_BASE_URL, | ||
CONF_API_KEY, | ||
CONF_TIMEOUT, | ||
CONF_MODEL, | ||
CONF_CTX_SIZE, | ||
CONF_MAX_TOKENS, | ||
CONF_MIROSTAT_MODE, | ||
CONF_MIROSTAT_ETA, | ||
CONF_MIROSTAT_TAU, | ||
CONF_TEMPERATURE, | ||
CONF_REPEAT_PENALTY, | ||
CONF_TOP_K, | ||
CONF_TOP_P, | ||
CONF_PROMPT_SYSTEM, | ||
|
||
DEFAULT_BASE_URL, | ||
DEFAULT_API_KEY, | ||
DEFAULT_TIMEOUT, | ||
DEFAULT_MODEL, | ||
DEFAULT_CTX_SIZE, | ||
DEFAULT_MAX_TOKENS, | ||
DEFAULT_MIROSTAT_MODE, | ||
DEFAULT_MIROSTAT_ETA, | ||
DEFAULT_MIROSTAT_TAU, | ||
DEFAULT_TEMPERATURE, | ||
DEFAULT_REPEAT_PENALTY, | ||
DEFAULT_TOP_K, | ||
DEFAULT_TOP_P, | ||
DEFAULT_PROMPT_SYSTEM | ||
) | ||
from .exceptions import ( | ||
ApiClientError, | ||
ApiCommError, | ||
ApiTimeoutError | ||
) | ||
|
||
|
||
STEP_USER_DATA_SCHEMA = vol.Schema( | ||
{ | ||
vol.Required(CONF_BASE_URL, default=DEFAULT_BASE_URL): str, | ||
vol.Required(CONF_API_KEY, default=DEFAULT_API_KEY): str, | ||
vol.Required(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): int, | ||
} | ||
) | ||
|
||
DEFAULT_OPTIONS = types.MappingProxyType( | ||
{ | ||
CONF_BASE_URL: DEFAULT_BASE_URL, | ||
CONF_API_KEY: DEFAULT_API_KEY, | ||
CONF_TIMEOUT: DEFAULT_TIMEOUT, | ||
CONF_MODEL: DEFAULT_MODEL, | ||
CONF_PROMPT_SYSTEM: DEFAULT_PROMPT_SYSTEM | ||
} | ||
) | ||
|
||
|
||
class MeeseeksConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
"""Handle a config flow for Meeseeks Conversation. Handles UI wizard.""" | ||
|
||
VERSION = 1 | ||
|
||
async def async_step_user( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Handle the initial step.""" | ||
if user_input is None: | ||
return self.async_show_form( | ||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA | ||
) | ||
|
||
# Search for duplicates with the same CONF_BASE_URL value. | ||
for existing_entry in self._async_current_entries(include_ignore=False): | ||
if existing_entry.data.get(CONF_BASE_URL) == user_input[CONF_BASE_URL]: | ||
return self.async_abort(reason="already_configured") | ||
|
||
errors = {} | ||
try: | ||
self.client = MeeseeksApiClient( | ||
base_url=cv.url_no_path(user_input[CONF_BASE_URL]), | ||
timeout=user_input[CONF_TIMEOUT], | ||
session=async_create_clientsession(self.hass), | ||
) | ||
response = await self.client.async_get_heartbeat() | ||
if not response: | ||
raise vol.Invalid("Invalid Meeseeks server") | ||
# except vol.Invalid: | ||
# errors["base"] = "invalid_url" | ||
# except ApiTimeoutError: | ||
# errors["base"] = "timeout_connect" | ||
# except ApiCommError: | ||
# errors["base"] = "cannot_connect" | ||
# except ApiClientError as exception: | ||
# LOGGER.exception("Unexpected exception: %s", exception) | ||
# errors["base"] = "unknown" | ||
except Exception as exception: | ||
LOGGER.exception("Unexpected exception: %s", exception) | ||
errors["base"] = "unknown" | ||
else: | ||
return self.async_create_entry( | ||
title=f"Meeseeks - {user_input[CONF_BASE_URL]}", | ||
data={ | ||
CONF_BASE_URL: user_input[CONF_BASE_URL] | ||
}, | ||
options={ | ||
CONF_TIMEOUT: user_input[CONF_TIMEOUT] | ||
} | ||
) | ||
|
||
return self.async_show_form( | ||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors | ||
) | ||
|
||
@staticmethod | ||
def async_get_options_flow( | ||
config_entry: config_entries.ConfigEntry, | ||
) -> config_entries.OptionsFlow: | ||
"""Create the options flow.""" | ||
return MeeseeksOptionsFlow(config_entry) | ||
|
||
|
||
class MeeseeksOptionsFlow(config_entries.OptionsFlow): | ||
"""Meeseeks config flow options handler.""" | ||
|
||
def __init__(self, config_entry: config_entries.ConfigEntry) -> None: | ||
"""Initialize options flow.""" | ||
self.config_entry = config_entry | ||
self.options = dict(config_entry.options) | ||
|
||
async def async_step_init( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Manage the options.""" | ||
return self.async_show_menu( | ||
step_id="init", | ||
menu_options=MENU_OPTIONS | ||
) | ||
|
||
async def async_step_all_set( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Manage the options.""" | ||
return self.async_show_menu( | ||
step_id="init", | ||
menu_options=MENU_OPTIONS | ||
) | ||
|
||
async def async_step_general_config( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Manage the options.""" | ||
return self.async_show_menu( | ||
step_id="init", | ||
menu_options=MENU_OPTIONS | ||
) | ||
|
||
async def async_step_prompt_system( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Manage the options.""" | ||
return self.async_show_menu( | ||
step_id="init", | ||
menu_options=MENU_OPTIONS | ||
) | ||
|
||
async def async_step_model_config( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Manage the options.""" | ||
return self.async_show_menu( | ||
step_id="init", | ||
menu_options=MENU_OPTIONS | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
"""Constants for meeseeks_conversation.""" | ||
from logging import Logger, getLogger | ||
|
||
LOGGER: Logger = getLogger(__package__) | ||
|
||
NAME = "Meeseeks" | ||
DOMAIN = "meeseeks_conversation" | ||
|
||
MENU_OPTIONS = ["all_set"] | ||
# MENU_OPTIONS = ["general_config", "model_config", "prompt_system"] | ||
|
||
CONF_BASE_URL = "base_url" | ||
CONF_API_KEY = "api_key" | ||
CONF_TIMEOUT = "timeout" | ||
CONF_MODEL = "chat_model" | ||
CONF_CTX_SIZE = "ctx_size" | ||
CONF_MAX_TOKENS = "max_tokens" | ||
CONF_MIROSTAT_MODE = "mirostat_mode" | ||
CONF_MIROSTAT_ETA = "mirostat_eta" | ||
CONF_MIROSTAT_TAU = "mirostat_tau" | ||
CONF_TEMPERATURE = "temperature" | ||
CONF_REPEAT_PENALTY = "repeat_penalty" | ||
CONF_TOP_K = "top_k" | ||
CONF_TOP_P = "top_p" | ||
CONF_PROMPT_SYSTEM = "prompt" | ||
|
||
DEFAULT_BASE_URL = "http://meeseeks.server:5123" | ||
DEFAULT_API_KEY = "msk-strong-password" | ||
DEFAULT_TIMEOUT = 60 | ||
DEFAULT_MODEL = "llama2:latest" | ||
DEFAULT_CTX_SIZE = 2048 | ||
DEFAULT_MAX_TOKENS = 128 | ||
DEFAULT_MIROSTAT_MODE = "0" | ||
DEFAULT_MIROSTAT_ETA = 0.1 | ||
DEFAULT_MIROSTAT_TAU = 5.0 | ||
DEFAULT_TEMPERATURE = 0.8 | ||
DEFAULT_REPEAT_PENALTY = 1.1 | ||
DEFAULT_TOP_K = 40 | ||
DEFAULT_TOP_P = 0.9 | ||
|
||
DEFAULT_PROMPT_SYSTEM = "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
"""DataUpdateCoordinator for meeseeks_conversation.""" | ||
from __future__ import annotations | ||
|
||
from datetime import timedelta | ||
|
||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.update_coordinator import ( | ||
DataUpdateCoordinator, | ||
UpdateFailed, | ||
) | ||
|
||
from .api import MeeseeksApiClient | ||
from .const import DOMAIN, LOGGER | ||
from .exceptions import ApiClientError | ||
|
||
|
||
# https://developers.home-assistant.io/docs/integration_fetching_data#coordinated-single-api-poll-for-data-for-all-entities | ||
class MeeseeksDataUpdateCoordinator(DataUpdateCoordinator): | ||
"""Class to manage fetching data from the API.""" | ||
|
||
config_entry: ConfigEntry | ||
|
||
def __init__( | ||
self, | ||
hass: HomeAssistant, | ||
client: MeeseeksApiClient, | ||
) -> None: | ||
"""Initialize.""" | ||
self.client = client | ||
super().__init__( | ||
hass=hass, | ||
logger=LOGGER, | ||
name=DOMAIN, | ||
update_interval=timedelta(minutes=5), | ||
) | ||
|
||
async def _async_update_data(self): | ||
"""Update data via library.""" | ||
try: | ||
return await self.client.async_get_heartbeat() | ||
except ApiClientError as exception: | ||
raise UpdateFailed(exception) from exception |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
"""The exceptions used by Extended OpenAI Conversation.""" | ||
from homeassistant.exceptions import HomeAssistantError | ||
|
||
class ApiClientError(HomeAssistantError): | ||
"""Exception to indicate a general API error.""" | ||
|
||
class ApiCommError(ApiClientError): | ||
"""Exception to indicate a communication error.""" | ||
|
||
class ApiJsonError(ApiClientError): | ||
"""Exception to indicate an error with json response.""" | ||
|
||
class ApiTimeoutError(ApiClientError): | ||
"""Exception to indicate a timeout error.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
"""Helper functions for Meeseeks.""" | ||
|
||
from homeassistant.components.conversation import DOMAIN as CONVERSATION_DOMAIN | ||
from homeassistant.components.homeassistant.exposed_entities import async_should_expose | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers import entity_registry | ||
|
||
|
||
def get_exposed_entities(hass: HomeAssistant) -> list[dict]: | ||
"""Return exposed entities.""" | ||
hass_entity = entity_registry.async_get(hass) | ||
exposed_entities: list[dict] = [] | ||
|
||
for state in hass.states.async_all(): | ||
if async_should_expose(hass, CONVERSATION_DOMAIN, state.entity_id): | ||
entity = hass_entity.async_get(state.entity_id) | ||
exposed_entities.append({ | ||
"entity_id": state.entity_id, | ||
"name": state.name, | ||
"state": state.state, | ||
"aliases": entity.aliases if entity else [], | ||
}) | ||
|
||
return exposed_entities |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"domain": "meeseeks_conversation", | ||
"name": "Meeseeks", | ||
"codeowners": [ | ||
"@bearlike" | ||
], | ||
"config_flow": true, | ||
"dependencies": [ | ||
"conversation" | ||
], | ||
"documentation": "https://github.com/bearlike/Personal-Assistant", | ||
"integration_type": "service", | ||
"iot_class": "cloud_polling", | ||
"issue_tracker": "https://github.com/bearlike/Personal-Assistant/issues", | ||
"version": "v1.2.0" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"config": { | ||
"step": { | ||
"user": { | ||
"data": { | ||
"base_url": "Base URL", | ||
"api_key": "API Key", | ||
"timeout": "API Timeout" | ||
} | ||
} | ||
}, | ||
"error": { | ||
"cannot_connect": "Unable to connect", | ||
"invalid_url": "Invalid URL", | ||
"timeout_connect": "Timeout while establishing connection", | ||
"unknown": "Unexpected error, please check logs" | ||
}, | ||
"abort": { | ||
"already_configured": "Service is already exist and configured" | ||
} | ||
}, | ||
"options": { | ||
"step": { | ||
"init": { | ||
"menu_options": { | ||
"general_config": "General Settings", | ||
"prompt_system": "System Prompt", | ||
"all_set": "Nothing to configure. You're all set!" | ||
} | ||
}, | ||
"general_config": { | ||
"title": "General Settings", | ||
"data": { | ||
"timeout": "API Timeout" | ||
} | ||
}, | ||
"prompt_system": { | ||
"title": "System Prompt" | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
{ | ||
"config": { | ||
"step": { | ||
"user": { | ||
"data": { | ||
"base_url": "Base URL", | ||
"api_key": "API Key", | ||
"timeout": "API Timeout" | ||
} | ||
} | ||
}, | ||
"error": { | ||
"cannot_connect": "Failed to connect", | ||
"invalid_url": "Invalid URL", | ||
"timeout_connect": "Timeout establishing connection", | ||
"unknown": "Unexpected error, check logs" | ||
}, | ||
"abort": { | ||
"already_configured": "Service is already configured" | ||
} | ||
}, | ||
"options": { | ||
"step": { | ||
"init": { | ||
"menu_options": { | ||
"general_config": "General Settings", | ||
"model_config": "Model Configuration", | ||
"prompt_system": "System Prompt", | ||
"all_set": "Nothing to configure. You're all set!" | ||
} | ||
}, | ||
"general_config": { | ||
"title": "General Settings", | ||
"data": { | ||
"timeout": "API Timeout" | ||
} | ||
}, | ||
"model_config": { | ||
"title": "Model Configuration", | ||
"data": { | ||
"chat_model": "Model", | ||
"ctx_size": "Context Size", | ||
"max_tokens": "Maximum Tokens", | ||
"mirostat_mode": "Mirostat Mode", | ||
"mirostat_eta": "Mirostat ETA", | ||
"mirostat_tau": "Mirostat TAU", | ||
"repeat_penalty": "Repeat Penalty", | ||
"temperature": "Temperature", | ||
"top_p": "Top P", | ||
"top_k": "Top K" | ||
} | ||
}, | ||
"prompt_system": { | ||
"title": "System Prompt" | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters