From afa2cbcc8599ae662ddb0a82698db6664b42fbc0 Mon Sep 17 00:00:00 2001 From: endolith Date: Sat, 29 Mar 2025 13:40:42 -0400 Subject: [PATCH 1/8] Break up lines for dummy class definition to clarify indentation --- interpreter/profiles.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interpreter/profiles.py b/interpreter/profiles.py index 140361c14c..ac4c4dac00 100644 --- a/interpreter/profiles.py +++ b/interpreter/profiles.py @@ -147,7 +147,9 @@ def load(self, path): # This avoids loading the full interpreter module which is resource intensive content = content.replace( "from interpreter import interpreter", - "class Interpreter:\n pass\ninterpreter = Interpreter()", + "class Interpreter:\n" + " pass\n" + "interpreter = Interpreter()", ) # Execute the modified profile content From cf8fe5e2fc513dc0aa5ff559ad08483239dddf93 Mon Sep 17 00:00:00 2001 From: endolith Date: Sat, 29 Mar 2025 14:18:37 -0400 Subject: [PATCH 2/8] Replace .json profile references with .py Profiles were changed from .json to .py files in bee3f3c3, but the documentation was not updated everywhere. --- interpreter/__init__.py | 4 ++-- interpreter/interpreter.py | 10 +++++----- interpreter/misc/welcome.py | 8 ++++---- interpreter/profiles.py | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/interpreter/__init__.py b/interpreter/__init__.py index 7ed72105b5..97f9df1d5c 100644 --- a/interpreter/__init__.py +++ b/interpreter/__init__.py @@ -18,11 +18,11 @@ interpreter = Interpreter() # Load from custom profile -config = Config.from_file("~/custom_profile.json") +config = Config.from_file("~/custom_profile.py") interpreter = Interpreter(config) # Save current settings -interpreter.save_config("~/my_settings.json") +interpreter.save_config("~/my_settings.py") """ # Use lazy imports to avoid loading heavy modules immediately diff --git a/interpreter/interpreter.py b/interpreter/interpreter.py index ab3cfdd3cd..25648e6cd5 100644 --- a/interpreter/interpreter.py +++ b/interpreter/interpreter.py @@ -99,11 +99,11 @@ class Interpreter: # With custom configuration from interpreter import Profile - profile = Profile.from_file("~/custom_profile.json") + profile = Profile.from_file("~/custom_profile.py") interpreter = Interpreter(profile) # Save settings for later - interpreter.save_profile("~/my_settings.json") + interpreter.save_profile("~/my_settings.py") Parameters ---------- @@ -152,7 +152,7 @@ def load_profile(self, path): Load settings from a profile file Example: - >>> interpreter.load_profile("~/work_settings.json") + >>> interpreter.load_profile("~/work_settings.py") """ self._profile.load(path) # Update interpreter attributes from new profile @@ -164,7 +164,7 @@ def save_profile(self, path=None): Save current settings as a profile Example: - >>> interpreter.save_profile("~/my_preferred_settings.json") + >>> interpreter.save_profile("~/my_preferred_settings.py") """ # Update profile object with current values self._profile.from_dict(self.to_dict()) @@ -177,7 +177,7 @@ def from_profile(cls, path): Create new interpreter instance from a profile file Example: - >>> interpreter = Interpreter.from_profile("~/work_settings.json") + >>> interpreter = Interpreter.from_profile("~/work_settings.py") """ return cls(Profile.from_file(path)) diff --git a/interpreter/misc/welcome.py b/interpreter/misc/welcome.py index 512f577abe..220d301757 100644 --- a/interpreter/misc/welcome.py +++ b/interpreter/misc/welcome.py @@ -254,9 +254,9 @@ def welcome_message(): --serve Start in server mode example: interpreter "create a python script" -example: interpreter -m gpt-4 "analyze data.csv" +example: interpreter -m gpt-4 "analyze data.csv" example: interpreter --auto-run "install nodejs" -example: interpreter --profile work.json +example: interpreter --profile work.py """ ) @@ -282,9 +282,9 @@ def welcome_message(): --serve Start in server mode example: interpreter "create a python script" -example: interpreter -m gpt-4 "analyze data.csv" +example: interpreter -m gpt-4 "analyze data.csv" example: interpreter --auto-run "install nodejs" -example: interpreter --profile work.json +example: interpreter --profile work.py """ ) diff --git a/interpreter/profiles.py b/interpreter/profiles.py index ac4c4dac00..465793969e 100644 --- a/interpreter/profiles.py +++ b/interpreter/profiles.py @@ -20,10 +20,10 @@ class Profile: profile = Profile() # Load from specific profile - profile = Profile.from_file("~/custom_profile.json") + profile = Profile.from_file("~/custom_profile.py") # Save current settings - profile.save("~/my_settings.json") + profile.save("~/my_settings.py") """ DEFAULT_PROFILE_FOLDER = "~/.openinterpreter" From fbcc7174f4bd1b24f19ed7fbd7a6fe3f3ca5372d Mon Sep 17 00:00:00 2001 From: endolith Date: Sat, 29 Mar 2025 14:29:21 -0400 Subject: [PATCH 3/8] Fix doctest examples format --- interpreter/__init__.py | 17 ++++++++++------- interpreter/interpreter.py | 21 ++++++++++++--------- interpreter/profiles.py | 15 +++++++++------ 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/interpreter/__init__.py b/interpreter/__init__.py index 97f9df1d5c..91afceb605 100644 --- a/interpreter/__init__.py +++ b/interpreter/__init__.py @@ -14,15 +14,18 @@ ------------ >>> from interpreter import Interpreter, Config -# Use defaults -interpreter = Interpreter() +Use defaults: -# Load from custom profile -config = Config.from_file("~/custom_profile.py") -interpreter = Interpreter(config) +>>> interpreter = Interpreter() + +Load from custom profile: + +>>> config = Config.from_file("~/custom_profile.py") +>>> interpreter = Interpreter(config) + +Save current settings: -# Save current settings -interpreter.save_config("~/my_settings.py") +>>> interpreter.save_config("~/my_settings.py") """ # Use lazy imports to avoid loading heavy modules immediately diff --git a/interpreter/interpreter.py b/interpreter/interpreter.py index 25648e6cd5..61cccce65b 100644 --- a/interpreter/interpreter.py +++ b/interpreter/interpreter.py @@ -93,17 +93,20 @@ class Interpreter: -------- >>> from interpreter import Interpreter - # Basic usage - interpreter = Interpreter() - interpreter.chat() + Basic usage: - # With custom configuration - from interpreter import Profile - profile = Profile.from_file("~/custom_profile.py") - interpreter = Interpreter(profile) + >>> interpreter = Interpreter() + >>> interpreter.chat() - # Save settings for later - interpreter.save_profile("~/my_settings.py") + With custom configuration: + + >>> from interpreter import Profile + >>> profile = Profile.from_file("~/custom_profile.py") + >>> interpreter = Interpreter(profile) + + Save settings for later: + + >>> interpreter.save_profile("~/my_settings.py") Parameters ---------- diff --git a/interpreter/profiles.py b/interpreter/profiles.py index 465793969e..2bf0f0e6ea 100644 --- a/interpreter/profiles.py +++ b/interpreter/profiles.py @@ -16,14 +16,17 @@ class Profile: -------- >>> from interpreter import Profile - # Load defaults (and ~/.openinterpreter if it exists) - profile = Profile() + Load defaults (and ~/.openinterpreter if it exists): - # Load from specific profile - profile = Profile.from_file("~/custom_profile.py") + >>> profile = Profile() - # Save current settings - profile.save("~/my_settings.py") + Load from specific profile: + + >>> profile = Profile.from_file("~/custom_profile.py") + + Save current settings: + + >>> profile.save("~/my_settings.py") """ DEFAULT_PROFILE_FOLDER = "~/.openinterpreter" From f61ac3ddfef0e28fea913a4a7ff88f5ea29e23f4 Mon Sep 17 00:00:00 2001 From: endolith Date: Sat, 29 Mar 2025 18:23:13 -0400 Subject: [PATCH 4/8] Update reference to "Config" which doesn't exist Possibly related to 4e97cd77 --- interpreter/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interpreter/__init__.py b/interpreter/__init__.py index 91afceb605..688fd4aafa 100644 --- a/interpreter/__init__.py +++ b/interpreter/__init__.py @@ -12,7 +12,7 @@ Configuration ------------ ->>> from interpreter import Interpreter, Config +>>> from interpreter import Interpreter, Profile Use defaults: @@ -20,12 +20,12 @@ Load from custom profile: ->>> config = Config.from_file("~/custom_profile.py") ->>> interpreter = Interpreter(config) +>>> profile = Profile.from_file("~/custom_profile.py") +>>> interpreter = Interpreter(profile) Save current settings: ->>> interpreter.save_config("~/my_settings.py") +>>> interpreter.save_profile("~/my_settings.py") """ # Use lazy imports to avoid loading heavy modules immediately From f2a1446feff306df4ef8534fa7d13e2fdc4cfb3f Mon Sep 17 00:00:00 2001 From: endolith Date: Sat, 29 Mar 2025 18:49:26 -0400 Subject: [PATCH 5/8] Clarify comment about default profile There is no file called default_profile.py. I'm not sure why we would imply that it exists, and allow it to be used, but just clarify the comment for now. --- interpreter/profiles.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interpreter/profiles.py b/interpreter/profiles.py index 2bf0f0e6ea..2113afe67a 100644 --- a/interpreter/profiles.py +++ b/interpreter/profiles.py @@ -132,7 +132,8 @@ def load(self, path): path += ".py" if not os.path.exists(path): - # If file doesn't exist, if it's the default, that's fine + # If the missing file is the default profile path, that's fine - + # we'll use the defaults from __init__ if os.path.abspath(os.path.expanduser(path)) == os.path.abspath( os.path.expanduser(self.DEFAULT_PROFILE_PATH) ): From 7013f817df0bdd69ff9ef4096f9d57cf4b0f33e0 Mon Sep 17 00:00:00 2001 From: endolith Date: Sat, 29 Mar 2025 20:08:01 -0400 Subject: [PATCH 6/8] Remove unused imports Likely left over from bee3f3c3 --- interpreter/profiles.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/interpreter/profiles.py b/interpreter/profiles.py index 2113afe67a..f2b003be9d 100644 --- a/interpreter/profiles.py +++ b/interpreter/profiles.py @@ -1,8 +1,5 @@ -import json import os -import platform import sys -from datetime import datetime class Profile: From dec7faa32092803504f25ab2737cb8dcc4eeae43 Mon Sep 17 00:00:00 2001 From: endolith Date: Sun, 30 Mar 2025 12:10:49 -0400 Subject: [PATCH 7/8] Update profile.profile_path to the actual path Previously it was keeping the default: profile_path: ~/.openinterpreter\default_profile.py because interpreter.profile_path is not an attribute that would be set within the profile file, but is a property *of* that file. --- interpreter/profiles.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interpreter/profiles.py b/interpreter/profiles.py index f2b003be9d..e522ca65e0 100644 --- a/interpreter/profiles.py +++ b/interpreter/profiles.py @@ -69,7 +69,7 @@ def __init__(self): # Debug settings self.debug = False # Whether to enable debug mode - # Set default path but don't load from it + # Initialize with default path (which may be overridden in from_file) self.profile_path = self.DEFAULT_PROFILE_PATH def to_dict(self): @@ -136,6 +136,9 @@ def load(self, path): ): return raise FileNotFoundError(f"Profile file not found at {path}") + else: + # Update profile_path when loading succeeds + self.profile_path = os.path.abspath(path) # Create a temporary namespace to execute the profile in namespace = {} @@ -171,4 +174,6 @@ def from_file(cls, path): """Create a new profile instance from a file""" profile = cls() profile.load(path) + # Update profile_path after successful load + profile.profile_path = os.path.abspath(os.path.expanduser(path)) return profile From daec1b3428c8c16377851d38d15b8896ac97a3f1 Mon Sep 17 00:00:00 2001 From: endolith Date: Sun, 30 Mar 2025 12:38:56 -0400 Subject: [PATCH 8/8] Prevent profile from being overridden by defaults Previously, when loading a custom profile via --profile, its settings could be overridden by defaults from the initial Profile() instance because argparse was initialized with those defaults, and only None-valued arguments would be updated from the custom profile. This caused issues like custom model settings being reset to the default claude-3-5-sonnet-20241022. This change: - Creates a fresh Profile instance when loading custom profiles - Tracks which arguments were explicitly provided via CLI - Updates args from profile unless explicitly set via CLI This ensures profile settings take precedence over defaults while still allowing CLI arguments to override profile settings. Example: Before: Custom profile with model="gpt-4o-mini" would be overridden by default model="claude-3-5-sonnet-20241022" unless --model was explicitly set on command line. After: Custom profile settings are preserved unless explicitly overridden by command line arguments. The provider auto-detection still works as expected: - If profile sets both model and provider: uses those values - If profile sets only model: provider=None allows auto-detection --- interpreter/cli.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interpreter/cli.py b/interpreter/cli.py index 26b011dcb6..6a391e22b2 100644 --- a/interpreter/cli.py +++ b/interpreter/cli.py @@ -270,10 +270,16 @@ def parse_args(): subprocess.run([opener, profile_dir]) sys.exit(0) + # If custom profile specified, load it and update args if args["profile"] != profile.profile_path: - profile.load(args["profile"]) + # Load the specified profile, ignoring any defaults + profile = Profile.from_file(args["profile"]) + # Find which settings were explicitly set via command line flags + cli_provided = {k for k,v in parser._option_string_actions.items() + if v.dest in args and sys.argv.__contains__(k)} + # Update args with profile values, preserving any CLI overrides for key, value in vars(profile).items(): - if key in args and args[key] is None: + if key in args and key not in cli_provided: args[key] = value if args["save"]: