From 31c86a256fb06239946763f66cdc50959feed044 Mon Sep 17 00:00:00 2001 From: Denis Shulyaka Date: Thu, 12 Dec 2024 16:26:54 +0300 Subject: [PATCH] Run syncronous functions in a separate thread --- README.md | 2 +- custom_components/powerllm/llm_tools.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f81af1..4dbbe64 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ There are two options: * Extend the `custom_components.powerllm.PowerLLMTool` class to implement the functionality, then call `custom_components.powerllm.async_register_tool` to register the object of the class. See the [memory tool](https://github.com/Shulyaka/powerllm/blob/master/custom_components/powerllm/tools/memory.py) for an example -* Use the `custom_components.powerllm.llm_tool` decorator for any python function. The function is recommended to have type annotations for all parameters. If a parameter name is "hass", "llm_context", or any of the `homeassistant.helpers.llm.LLMContext` attributes, then the value for that parameter will be provided by the conversation agent ("pytest-style"). All other arguments will be provided by the LLM. Refer to the [python code tool](https://github.com/Shulyaka/powerllm/blob/master/custom_components/powerllm/tools/python_code.py) as an example. +* Use the `custom_components.powerllm.llm_tool` decorator for any python function. The function is recommended to have type annotations for all parameters. If a parameter name is "hass", "llm_context", or any of the `homeassistant.helpers.llm.LLMContext` attributes, then the value for that parameter will be provided by the conversation agent ("pytest-style"). All other arguments will be provided by the LLM. Refer to the [python code tool](https://github.com/Shulyaka/powerllm/blob/master/custom_components/powerllm/tools/python_code.py) as an example. A synchronous function will be executed in a separate thread, while an async function will be run concurrently in the main event loop and therefore is not allowed to use any blocking operations. The tools in this repository use various techniques for demonstration. diff --git a/custom_components/powerllm/llm_tools.py b/custom_components/powerllm/llm_tools.py index b53ad11..47bd258 100644 --- a/custom_components/powerllm/llm_tools.py +++ b/custom_components/powerllm/llm_tools.py @@ -2,6 +2,7 @@ from __future__ import annotations +import asyncio import inspect import logging from collections.abc import Callable @@ -289,6 +290,8 @@ class PowerFunctionTool(PowerLLMTool): attributes, then the value for that parameter will be provided by the conversation agent 'pytest-style'. All other arguments will be provided by the LLM. + Async functions are not allowed to use any blocking code, while a synchronous + function will be executed in a separate thread. """ function: Callable @@ -373,7 +376,11 @@ async def async_call( if inspect.iscoroutinefunction(self.function): return await self.function(**kwargs) - return self.function(**kwargs) + + if hass.loop != asyncio.get_running_loop(): + return self.function(**kwargs) + + return await hass.loop.run_in_executor(None, lambda: self.function(**kwargs)) @callback