diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 78ae0ce..38e890a 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -86,7 +86,7 @@ If you do not want to use LiteLLM at all, the framework allows you to inject any 3. In the CLI wizard, select `custom_function` as the Model Type. 4. Provide the path to `my_model.py` and the exact name of the function you wrote. -The framework will dynamically import your file at runtime and use it exclusively for that agent's turns. +The framework will dynamically import your file at runtime and use it exclusively for that agent's turns. Note that custom functions are executed with a timeout boundary (using the agent's configured `timeout` setting) to prevent infinite UI hangups. ## CI/CD and Robustness To ensure the framework remains stable as it grows, we maintain a comprehensive CI/CD pipeline using **GitHub Actions**. Every contribution is automatically tested against Python 3.13 for: diff --git a/rooms/agent.py b/rooms/agent.py index bb801ac..fec3a81 100644 --- a/rooms/agent.py +++ b/rooms/agent.py @@ -3,6 +3,7 @@ import importlib.util import sys import os +import concurrent.futures from typing import Optional, List, Dict, Any from .config import AgentConfig, ModelType @@ -44,8 +45,13 @@ def _execute_custom_function(self, messages: List[Dict[str, str]]) -> str: inference_func = getattr(module, func_name) # We enforce standard (messages: List[Dict]) -> str signature for custom functions - response = inference_func(messages) - return str(response).strip() + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: + future = executor.submit(inference_func, messages) + try: + response = future.result(timeout=self.config.timeout) + return str(response).strip() + except concurrent.futures.TimeoutError: + return f"[Timeout Error: The custom function '{func_name}' took too long to respond ({self.config.timeout}s)]" except AttributeError: return f"[Error: Function '{func_name}' not found in {file_path}]"