From f655682e0d57674f429737574fecda9c610b8d9b Mon Sep 17 00:00:00 2001 From: martin legrand Date: Mon, 17 Mar 2025 09:55:49 +0100 Subject: [PATCH 1/2] Feat : better error handling in llm_provider --- sources/browser.py | 2 +- sources/llm_provider.py | 51 +++++++++++++++++++++++++++------------ sources/text_to_speech.py | 3 ++- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/sources/browser.py b/sources/browser.py index 06c0c6c..ea10fec 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -34,7 +34,7 @@ class Browser: self.logger.info("Browser initialized successfully") except Exception as e: raise Exception(f"Failed to initialize browser: {str(e)}") - + def go_to(self, url): """Navigate to a specified URL.""" try: diff --git a/sources/llm_provider.py b/sources/llm_provider.py index 80a85d3..7b87415 100644 --- a/sources/llm_provider.py +++ b/sources/llm_provider.py @@ -10,6 +10,7 @@ from dotenv import load_dotenv, set_key from openai import OpenAI from huggingface_hub import InferenceClient import os +import httpx class Provider: def __init__(self, provider_name, model, server_address = "127.0.0.1:5000"): @@ -31,6 +32,9 @@ class Provider: self.get_api_key(self.provider_name) elif self.server != "": print("Provider", provider_name, "initialized at", self.server) + self.check_address_format(self.server) + if not self.is_ip_online(self.server.split(':')[0]): + raise Exception(f"Server at {self.server} is offline.") def get_api_key(self, provider): load_dotenv() @@ -53,7 +57,7 @@ class Provider: if not port.isdigit() or not (0 <= int(port) <= 65535): raise ValueError("Port must be a number between 0 and 65535.") except ValueError as e: - raise Exception(f"Invalid address format: {e}") + raise Exception(f"Invalid address format: {e}. Is port specified?") return address def respond(self, history, verbose = True): @@ -61,7 +65,14 @@ class Provider: Use the choosen provider to generate text. """ llm = self.available_providers[self.provider_name] - thought = llm(history, verbose) + try: + thought = llm(history, verbose) + except ConnectionError as e: + raise ConnectionError(f"{str(e)}\nConnection to {self.server} failed.") + except AttributeError as e: + raise NotImplementedError(f"{str(e)}\nIs {self.provider_name} implemented ?") + except Exception as e: + raise Exception(f"Provider {self.provider_name} failed: {str(e)}") from e return thought def is_ip_online(self, ip_address): @@ -71,20 +82,22 @@ class Provider: param = '-n' if platform.system().lower() == 'windows' else '-c' command = ['ping', param, '1', ip_address] try: - output = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5) + output = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=15) if output.returncode == 0: return True else: + print("errorcode:", output) return False except subprocess.TimeoutExpired: + print("timeout") return True except Exception as e: - print(f"An error occurred: {e}") + print(f"is_ip_online error:\n{e}") return False def server_fn(self, history, verbose = False): """ - Use a remote server wit LLM to generate text. + Use a remote server with LLM to generate text. """ thought = "" route_start = f"http://{self.server}/generate" @@ -92,13 +105,18 @@ class Provider: if not self.is_ip_online(self.server.split(":")[0]): raise Exception(f"Server is offline at {self.server}") - requests.post(route_start, json={"messages": history}) - is_complete = False - while not is_complete: - response = requests.get(f"http://{self.server}/get_updated_sentence") - thought = response.json()["sentence"] - is_complete = bool(response.json()["is_complete"]) - time.sleep(2) + try: + requests.post(route_start, json={"messages": history}) + is_complete = False + while not is_complete: + response = requests.get(f"http://{self.server}/get_updated_sentence") + thought = response.json()["sentence"] + is_complete = bool(response.json()["is_complete"]) + time.sleep(2) + except KeyError as e: + raise f"{str(e)}\n\nError occured with server route. Are you using the correct address for the config.ini provider?" + except Exception as e: + raise e return thought def ollama_fn(self, history, verbose = False): @@ -116,11 +134,14 @@ class Provider: if verbose: print(chunk['message']['content'], end='', flush=True) thought += chunk['message']['content'] + except httpx.ConnectError as e: + raise Exception("\nOllama connection failed. provider should not be set to ollama if server address is not localhost") from e except ollama.ResponseError as e: if e.status_code == 404: + print(f"Downloading {self.model}...") ollama.pull(self.model) - if "refused" in str(e): - raise Exception("Ollama connection failed. is the server running ?") + if "refused" in str(e).lower(): + raise Exception("Ollama connection failed. is the server running ?") from e raise e return thought @@ -155,7 +176,7 @@ class Provider: print(thought) return thought except Exception as e: - raise Exception(f"OpenAI API error: {e}") + raise Exception(f"OpenAI API error: {str(e)}") from e def test_fn(self, history, verbose = True): """ diff --git a/sources/text_to_speech.py b/sources/text_to_speech.py index fba0c41..58f716b 100644 --- a/sources/text_to_speech.py +++ b/sources/text_to_speech.py @@ -76,8 +76,9 @@ class Speech(): return parts[-1] if parts else path def shorten_paragraph(self, sentence): + #TODO find a better way, we would like to have the TTS not be annoying, speak only useful informations """ - Shorten paragraph like **explaination**: by keeping only the first sentence. + Find long paragraph like **explaination**: by keeping only the first sentence. Args: sentence (str): The sentence to shorten Returns: From 0ee6a651166e5f59ed072936d9c7c9b3774a400a Mon Sep 17 00:00:00 2001 From: martin legrand Date: Mon, 17 Mar 2025 10:17:02 +0100 Subject: [PATCH 2/2] .env example --- .env.example | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9f9d552 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +OPENAI_API_KEY='dont share this, not needed for local providers' +SERPAPI_KEY='dont share this, needed for internet search' +AVIATIONSTACK_API_KEY='not needed if you dont search for flight' \ No newline at end of file