mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Add websocket command to list LLM APIs (#177903)
This commit is contained in:
@@ -354,6 +354,7 @@ homeassistant.components.litejet.*
|
||||
homeassistant.components.litellm.*
|
||||
homeassistant.components.litterrobot.*
|
||||
homeassistant.components.llama_cpp.*
|
||||
homeassistant.components.llm.*
|
||||
homeassistant.components.local_ip.*
|
||||
homeassistant.components.local_todo.*
|
||||
homeassistant.components.lock.*
|
||||
|
||||
@@ -20,6 +20,7 @@ from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import DOMAIN
|
||||
from .websocket_api import async_setup as async_setup_ws_api
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -57,6 +58,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
hass, DOMAIN, _process_llm_tools_platform
|
||||
)
|
||||
async_register_api(hass, AssistAPI(hass))
|
||||
async_setup_ws_api(hass)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
"""Websocket API for the LLM integration."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.llm import async_get_apis
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup(hass: HomeAssistant) -> None:
|
||||
"""Set up the LLM websocket API."""
|
||||
websocket_api.async_register_command(hass, websocket_list_apis)
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command({vol.Required("type"): "llm/api/list"})
|
||||
@callback
|
||||
def websocket_list_apis(
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""List the registered LLM APIs.
|
||||
|
||||
Each API is described by the ID used to select it and the name shown to
|
||||
the user. APIs are listed in registration order.
|
||||
"""
|
||||
connection.send_result(
|
||||
msg["id"],
|
||||
{"apis": [{"id": api.id, "name": api.name} for api in async_get_apis(hass)]},
|
||||
)
|
||||
@@ -3297,6 +3297,16 @@ disallow_untyped_defs = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.llm.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
disallow_subclassing_any = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_decorators = true
|
||||
disallow_untyped_defs = true
|
||||
warn_return_any = true
|
||||
warn_unreachable = true
|
||||
|
||||
[mypy-homeassistant.components.local_ip.*]
|
||||
check_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
"""Tests for the LLM integration websocket API."""
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import llm
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
|
||||
class _StubAPI(llm.API):
|
||||
"""Minimal LLM API used to populate the registry."""
|
||||
|
||||
async def async_get_api_instance(
|
||||
self, llm_context: llm.LLMContext
|
||||
) -> llm.APIInstance:
|
||||
"""Return the instance of the API."""
|
||||
return llm.APIInstance(
|
||||
api=self, api_prompt="", llm_context=llm_context, tools=[]
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def setup_llm(hass: HomeAssistant) -> None:
|
||||
"""Set up the LLM integration."""
|
||||
assert await async_setup_component(hass, "llm", {})
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("registered_apis", "expected_apis"),
|
||||
[
|
||||
pytest.param([], [{"id": "assist", "name": "Assist"}], id="assist_only"),
|
||||
pytest.param(
|
||||
[("test-api", "Test API")],
|
||||
[
|
||||
{"id": "assist", "name": "Assist"},
|
||||
{"id": "test-api", "name": "Test API"},
|
||||
],
|
||||
id="registered_api",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_list_apis(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
registered_apis: list[tuple[str, str]],
|
||||
expected_apis: list[dict[str, str]],
|
||||
) -> None:
|
||||
"""Test listing the registered LLM APIs."""
|
||||
for api_id, name in registered_apis:
|
||||
llm.async_register_api(hass, _StubAPI(hass=hass, id=api_id, name=name))
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
await client.send_json_auto_id({"type": "llm/api/list"})
|
||||
response = await client.receive_json()
|
||||
|
||||
assert response["success"]
|
||||
assert response["result"] == {"apis": expected_apis}
|
||||
|
||||
|
||||
async def test_list_apis_requires_admin(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
hass_read_only_access_token: str,
|
||||
) -> None:
|
||||
"""Test listing the LLM APIs is only allowed for admins."""
|
||||
client = await hass_ws_client(hass, hass_read_only_access_token)
|
||||
|
||||
await client.send_json_auto_id({"type": "llm/api/list"})
|
||||
response = await client.receive_json()
|
||||
|
||||
assert not response["success"]
|
||||
assert response["error"]["code"] == "unauthorized"
|
||||
Reference in New Issue
Block a user