diff --git a/deck/llm.py b/deck/llm.py index c9cfc55..6b26df4 100644 --- a/deck/llm.py +++ b/deck/llm.py @@ -2,7 +2,8 @@ # Supports external config files and multiple providers # Created by Luxferre in 2025, released into public domain -import json +import json, re +from deck.time import * # init requests library in a platform-agnostic way requests = None @@ -82,6 +83,32 @@ class LLMChat: self.messages = [{"role": "system", "content": self.config.get('system_prompt', '')}] return (True, "Context cleared") + def guarded_post(url, headers={}, payload={}): + """ + Performs a POST request trying to bypass possible 429 errors with heuristics. + """ + response = requests.post(url, headers=headers, json=payload) + if response.status_code == 429: + restext = response.text + try: + resdata = response.json() + restext = data['error']['message'] + except: + pass + response.close() + # try and parse something like "please wait for 0.1 seconds" + sleeptime = 1.0 + try: + sleeptime = float(re.search(r'\d+\.\d+', restext).group(0)) + except: + pass + time.sleep(sleeptime) + return guarded_post(url, headers, payload) + else: + data = response.json() + response.close() + return data + def get_completion(self, user_input): """ Sends request to LLM. @@ -101,7 +128,7 @@ class LLMChat: url = f"{self.provider.get('base_url').rstrip('/')}/chat/completions" try: - response = requests.post(url, headers=self.headers, json=payload) + response = guarded_post(url, self.headers, payload) data = response.json() response.close() # Important for CircuitPython memory bot_content = data['choices'][0]['message']['content']