diff --git a/.vscode/content.py b/.vscode/content.py new file mode 100644 index 00000000..e01c9476 --- /dev/null +++ b/.vscode/content.py @@ -0,0 +1,3 @@ +def example(): + content = "Hello" + print(content) \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..7a2295fa --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-msedge", + "name": "Launch Microsoft Edge", + "request": "launch", + "runtimeArgs": [ + "--remote-debugging-port=9222" + ], + "url": "c:\\Users\\Jim\\.vscode\\extensions\\ms-edgedevtools.vscode-edge-devtools-2.1.9\\out\\startpage\\index.html", + "presentation": { + "hidden": true + } + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..1cbb8a5b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "DockerRun.DisableDockerrc": true, + "python.testing.unittestArgs": [ + "-v", + "-s", + ".", + "-p", + "*test.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": true +} \ No newline at end of file diff --git a/README.md b/README.md index 221b3fd8..e77d4319 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -ome + +
diff --git a/operate/.pie.py b/operate/.pie.py new file mode 100644 index 00000000..f179e8f2 --- /dev/null +++ b/operate/.pie.py @@ -0,0 +1,185 @@ +import os +import sys +try: + from dotenv import load_dotenv +except Exception as e: + raise ImportError("python-dotenv is required: pip install python-dotenv") from e + +try: + from ollama import Client +except Exception: + Client = None + +try: + from openai import OpenAI +except Exception: + try: + import openai as _openai + OpenAI = getattr(_openai, "OpenAI", None) or _openai + except Exception as e: + raise ImportError("openai package required: pip install openai") from e + +try: + import anthropic +except Exception: + anthropic = None + +try: + import google.generativeai as genai +except Exception: + genai = None + +from prompt_toolkit.shortcuts import input_dialog + + +class Config: + """ + Configuration class for managing settings. + + Attributes: + verbose (bool): Flag indicating whether verbose mode is enabled. + openai_api_key (str): API key for OpenAI. + google_api_key (str): API key for Google. + ollama_host (str): url to ollama running remotely. + """ + + _instance = None + + def __new__(cls): + if cls._instance is None: + cls._instance = super(Config, cls).__new__(cls) + return cls._instance + + def __init__(self): + load_dotenv() + self.verbose = False + self.openai_api_key = None + self.google_api_key = None + self.ollama_host = None + self.anthropic_api_key = None + self.qwen_api_key = None + + def initialize_openai(self): + if self.verbose: + print("[Config][initialize_openai]") + + api_key = self.openai_api_key or os.getenv("OPENAI_API_KEY") + client = OpenAI(api_key=api_key) + client.api_key = api_key + client.base_url = os.getenv( + "OPENAI_API_BASE_URL", getattr(client, "base_url", None) + ) + return client + + def initialize_qwen(self): + if self.verbose: + print("[Config][initialize_qwen]") + + api_key = self.qwen_api_key or os.getenv("QWEN_API_KEY") + client = OpenAI( + api_key=api_key, + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + ) + client.api_key = api_key + client.base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1" + return client + + def initialize_google(self): + if self.verbose: + print("[Config][initialize_google]") + + api_key = self.google_api_key or os.getenv("GOOGLE_API_KEY") + genai.configure(api_key=api_key, transport="rest") + model = genai.GenerativeModel("gemini-pro-vision") + return model + + def initialize_ollama(self): + if self.ollama_host: + if self.verbose: + print("[Config][initialize_ollama] using cached ollama host") + else: + if self.verbose: + print( + "[Config][initialize_ollama] no cached ollama host. Assuming ollama running locally." + ) + self.ollama_host = os.getenv("OLLAMA_HOST", None) + model = Client(host=self.ollama_host) + return model + + def initialize_anthropic(self): + api_key = self.anthropic_api_key or os.getenv("ANTHROPIC_API_KEY") + return anthropic.Anthropic(api_key=api_key) + + def validation(self, model, voice_mode): + """ + Validate the input parameters for the dialog operation. + """ + self.require_api_key( + "OPENAI_API_KEY", + "OpenAI API key", + model == "gpt-4" + or voice_mode + or model == "gpt-4-with-som" + or model == "gpt-4-with-ocr" + or model == "gpt-4.1-with-ocr" + or model == "o1-with-ocr", + ) + self.require_api_key("GOOGLE_API_KEY", "Google API key", model == "gemini-pro-vision") + self.require_api_key("ANTHROPIC_API_KEY", "Anthropic API key", model == "claude-3") + self.require_api_key("QWEN_API_KEY", "Qwen API key", model == "qwen-vl") + + def require_api_key(self, key_name, key_description, is_required): + key_exists = bool(os.environ.get(key_name)) + if self.verbose: + print("[Config] require_api_key", key_name, key_exists) + if is_required and not key_exists: + self.prompt_and_save_api_key(key_name, key_description) + + def prompt_and_save_api_key(self, key_name, key_description): + key_value = input_dialog( + title="API Key Required", text=f"Please enter your {key_description}:" + ).run() + + if key_value is None: # User pressed cancel or closed the dialog + sys.exit("Operation cancelled by user.") + + if key_value: + if key_name == "OPENAI_API_KEY": + self.openai_api_key = key_value + elif key_name == "GOOGLE_API_KEY": + self.google_api_key = key_value + elif key_name == "ANTHROPIC_API_KEY": + self.anthropic_api_key = key_value + elif key_name == "QWEN_API_KEY": + self.qwen_api_key = key_value + + self.save_api_key_to_env(key_name, key_value) + load_dotenv() # Reload environment variables + + @staticmethod + def save_api_key_to_env(key_name, key_value): + """ + Write or replace a key in the local .env file. + - If .env does not exist, it will be created. + - If the key exists, its value will be replaced. + """ + env_path = ".env" + lines = [] + if os.path.exists(env_path): + with open(env_path, "r", encoding="utf-8") as f: + lines = f.readlines() + + key_prefix = f"{key_name}=" + found = False + new_lines = [] + for line in lines: + if line.strip().startswith(f"{key_name}="): + new_lines.append(f"{key_prefix}{key_value}\n") + found = True + else: + new_lines.append(line) + if not found: + new_lines.append(f"{key_prefix}{key_value}\n") + + with open(env_path, "w", encoding="utf-8") as f: + f.writelines(new_lines) diff --git a/readme/key.png b/readme/key.png deleted file mode 100644 index a127a581..00000000 Binary files a/readme/key.png and /dev/null differ diff --git a/readme/self-operating-computer.png b/readme/self-operating-computer.png deleted file mode 100644 index 39f311e1..00000000 Binary files a/readme/self-operating-computer.png and /dev/null differ diff --git a/readme/terminal-access-1.png b/readme/terminal-access-1.png deleted file mode 100644 index 2d0cebe8..00000000 Binary files a/readme/terminal-access-1.png and /dev/null differ diff --git a/readme/terminal-access-2.png b/readme/terminal-access-2.png deleted file mode 100644 index 92df410e..00000000 Binary files a/readme/terminal-access-2.png and /dev/null differ