import json import re from typing import List import time import tiktoken from typing import Tuple, List, Union, Dict from pydantic import BaseModel, ValidationError import pickle class Node(BaseModel): name: str info: str class Dag(BaseModel): nodes: List[Node] edges: List[List[Node]] NUM_IMAGE_TOKEN = 1105 # Value set of screen of size 1920x1080 for openai vision def call_llm_safe(agent) -> Union[str, Dag]: # Retry if fails max_retries = 3 # Set the maximum number of retries attempt = 0 response = "" while attempt < max_retries: try: response = agent.get_response() break # If successful, break out of the loop except Exception as e: attempt += 1 print(f"Attempt {attempt} failed: {e}") if attempt == max_retries: print("Max retries reached. Handling failure.") time.sleep(1.0) return response def calculate_tokens(messages, num_image_token=NUM_IMAGE_TOKEN) -> Tuple[int, int]: num_input_images = 0 output_message = messages[-1] input_message = messages[:-1] input_string = """""" for message in input_message: input_string += message["content"][0]["text"] + "\n" if len(message["content"]) > 1: num_input_images += 1 input_text_tokens = get_input_token_length(input_string) input_image_tokens = num_image_token * num_input_images output_tokens = get_input_token_length(output_message["content"][0]["text"]) return (input_text_tokens + input_image_tokens), output_tokens # Code based on https://github.com/xlang-ai/OSWorld/blob/main/mm_agents/agent.py def parse_dag(text): pattern = r"(.*?)" match = re.search(pattern, text, re.DOTALL) if match: json_str = match.group(1) try: json_data = json.loads(json_str) return Dag(**json_data["dag"]) except json.JSONDecodeError: print("Error: Invalid JSON") return None except KeyError: print("Error: 'dag' key not found in JSON") return None except ValidationError as e: print(f"Error: Invalid data structure - {e}") return None else: print("Error: JSON not found") return None def parse_dag(text): """ Try extracting JSON from tags first; if not found, try ```json … ``` Markdown fences. """ def _extract(pattern): m = re.search(pattern, text, re.DOTALL) return m.group(1).strip() if m else None # 1) look for json_str = _extract(r"(.*?)") # 2) fallback to ```json … ``` if json_str is None: json_str = _extract(r"```json\s*(.*?)\s*```") if json_str is None: print("Error: JSON not found in either tags or ```json``` fence") return None try: payload = json.loads(json_str) except json.JSONDecodeError as e: print(f"Error: Invalid JSON ({e})") return None if "dag" not in payload: print("Error: 'dag' key not found in JSON") return None try: return Dag(**payload["dag"]) except ValidationError as e: print(f"Error: Invalid data structure - {e}") return None def parse_single_code_from_string(input_string): input_string = input_string.strip() if input_string.strip() in ["WAIT", "DONE", "FAIL"]: return input_string.strip() # This regular expression will match both ```code``` and ```python code``` # and capture the `code` part. It uses a non-greedy match for the content inside. pattern = r"```(?:\w+\s+)?(.*?)```" # Find all non-overlapping matches in the string matches = re.findall(pattern, input_string, re.DOTALL) # The regex above captures the content inside the triple backticks. # The `re.DOTALL` flag allows the dot `.` to match newline characters as well, # so the code inside backticks can span multiple lines. # matches now contains all the captured code snippets codes = [] for match in matches: match = match.strip() commands = [ "WAIT", "DONE", "FAIL", ] # fixme: updates this part when we have more commands if match in commands: codes.append(match.strip()) elif match.split("\n")[-1] in commands: if len(match.split("\n")) > 1: codes.append("\n".join(match.split("\n")[:-1])) codes.append(match.split("\n")[-1]) else: codes.append(match) if len(codes) <= 0: return "fail" return codes[0] def get_input_token_length(input_string): enc = tiktoken.encoding_for_model("gpt-4") tokens = enc.encode(input_string) return len(tokens) def sanitize_code(code): # This pattern captures the outermost double-quoted text if "\n" in code: pattern = r'(".*?")' # Find all matches in the text matches = re.findall(pattern, code, flags=re.DOTALL) if matches: # Replace the first occurrence only first_match = matches[0] code = code.replace(first_match, f'"""{first_match[1:-1]}"""', 1) return code def extract_first_agent_function(code_string): # Regular expression pattern to match 'agent' functions with any arguments, including nested parentheses pattern = r'agent\.[a-zA-Z_]+\((?:[^()\'"]|\'[^\']*\'|"[^"]*")*\)' # Find all matches in the string matches = re.findall(pattern, code_string) # Return the first match if found, otherwise return None return matches[0] if matches else None def load_knowledge_base(kb_path: str) -> Dict: try: with open(kb_path, "r") as f: return json.load(f) except Exception as e: print(f"Error loading knowledge base: {e}") return {} def load_embeddings(embeddings_path: str) -> Dict: try: with open(embeddings_path, "rb") as f: return pickle.load(f) except Exception as e: print(f"Error loading embeddings: {e}") return {} def save_embeddings(embeddings_path: str, embeddings: Dict): try: with open(embeddings_path, "wb") as f: pickle.dump(embeddings, f) except Exception as e: print(f"Error saving embeddings: {e}")