From 2abd415571f22923d69c3498680aabc413066bd9 Mon Sep 17 00:00:00 2001 From: Miles Georgi Date: Mon, 15 Sep 2025 11:44:50 -0700 Subject: [PATCH] wip --- Gemfile.lock | 43 ++++---- src/foobara/agent/accomplish_goal.rb | 146 ++++++++++++++------------- 2 files changed, 100 insertions(+), 89 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index fbfea6f..a25c328 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,7 +12,7 @@ GEM public_suffix (>= 2.0.2, < 7.0) ast (2.4.3) base64 (0.3.0) - bigdecimal (3.2.2) + bigdecimal (3.2.3) byebug (12.0.0) coderay (1.1.3) crack (1.0.0) @@ -47,20 +47,21 @@ GEM foobara-type-generator (< 2.0.0) foobara-typescript-react-command-form-generator (< 2.0.0) foobara-typescript-remote-command-generator (< 2.0.0) - foobara (0.1.1) + foobara (0.1.10) bigdecimal foobara-lru-cache (< 2.0.0) foobara-util (< 2.0.0) inheritable-thread-vars (< 2.0.0) foobara-ai (1.0.2) - foobara-anthropic-api (1.0.1) - foobara-cached-command - foobara-http-api-command + foobara-anthropic-api (1.0.3) + foobara (>= 0.1.3, < 2.0.0) + foobara-cached-command (< 2.0.0) + foobara-http-api-command (< 2.0.0) foobara-cached-command (1.0.0) foobara (< 2.0.0) - foobara-command-generator (0.0.3) - foobara - foobara-files-generator + foobara-command-generator (0.1.0) + foobara (>= 0.1.7, < 2.0.0) + foobara-files-generator (< 2.0.0) foobara-domain-generator (0.0.3) foobara foobara-files-generator @@ -69,7 +70,7 @@ GEM foobara-files-generator foobara-dotenv-loader (0.0.3) dotenv - foobara-empty-ruby-project-generator (1.0.1) + foobara-empty-ruby-project-generator (1.0.4) extract-repo (< 2.0.0) foobara (>= 0.1.1, < 2.0.0) foobara-files-generator (< 2.0.0) @@ -116,7 +117,7 @@ GEM foobara-resque-scheduler-connector-generator (0.0.2) foobara foobara-files-generator - foobara-rubocop-rules (1.0.1) + foobara-rubocop-rules (1.0.3) rubocop rubocop-rspec foobara-sh-cli-connector (1.1.0) @@ -132,8 +133,8 @@ GEM foobara-typescript-react-command-form-generator (1.1.0) foobara (>= 0.1.1, < 2.0.0) foobara-typescript-remote-command-generator (< 2.0.0) - foobara-typescript-remote-command-generator (1.1.0) - foobara (>= 0.1.1, < 2.0.0) + foobara-typescript-remote-command-generator (1.1.1) + foobara (>= 0.1.4, < 2.0.0) foobara-files-generator (< 2.0.0) foobara-util (1.0.2) formatador (1.2.0) @@ -154,7 +155,7 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) - hashdiff (1.2.0) + hashdiff (1.2.1) inheritable-thread-vars (0.0.3) io-console (0.8.1) irb (1.15.2) @@ -168,7 +169,7 @@ GEM rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) - lumberjack (1.4.0) + lumberjack (1.4.2) method_source (1.1.0) nenv (0.3.0) notiffany (0.1.3) @@ -182,7 +183,7 @@ GEM pp (0.6.2) prettyprint prettyprint (0.2.0) - prism (1.4.0) + prism (1.5.1) pry (0.15.2) coderay (~> 1.1) method_source (~> 1.0) @@ -205,7 +206,7 @@ GEM regexp_parser (2.11.2) reline (0.6.2) io-console (~> 0.5) - rexml (3.4.1) + rexml (3.4.4) rspec (3.13.1) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) @@ -222,7 +223,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.5) - rubocop (1.80.0) + rubocop (1.80.2) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -239,7 +240,7 @@ GEM rubocop-rake (0.7.1) lint_roller (~> 1.1) rubocop (>= 1.72.1) - rubocop-rspec (3.6.0) + rubocop-rspec (3.7.0) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) ruby-prof (1.7.2) @@ -254,9 +255,9 @@ GEM simplecov_json_formatter (0.1.4) stringio (3.1.7) thor (1.4.0) - unicode-display_width (3.1.5) - unicode-emoji (~> 4.0, >= 4.0.4) - unicode-emoji (4.0.4) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.1.0) vcr (6.3.1) base64 webmock (3.25.1) diff --git a/src/foobara/agent/accomplish_goal.rb b/src/foobara/agent/accomplish_goal.rb index 7d0ac41..487041a 100644 --- a/src/foobara/agent/accomplish_goal.rb +++ b/src/foobara/agent/accomplish_goal.rb @@ -38,31 +38,32 @@ class AccomplishGoal < Foobara::Command depends_on ListCommands def execute - unless list_commands_already_ran? - simulate_describe_list_commands_command - simulate_list_commands_run - end + simulate_list_commands_run unless list_commands_already_ran? until mission_accomplished? or given_up? or killed? - increment_command_calls check_if_too_many_calls - determine_next_command_and_inputs - run_next_command - end + unless command_already_described? + choose_describe_command_command_instead + end - if given_up? - add_given_up_error + if command_inputs_valid? + run_next_command + else + increment_and_check_retry_count + end end + add_given_up_error if given_up? + build_result end attr_accessor :next_command_name, :next_command_inputs, :next_command_raw_inputs, :mission_accomplished, :given_up, :next_command_class, :next_command, :command_outcome, :timed_out, :final_result, :final_message, :command_response, :delayed_command_name, - :command_calls, :killed + :command_calls, :killed, :retry_count, :error_outcome def list_commands_already_ran? context.command_log.any? { |log_entry| log_entry.command_name =~ /\bListCommands\z/ } @@ -123,32 +124,9 @@ def simulate_describe_command_run_for_all_commands # :nocov: end - RETRY_COUNT = 3 - - def determine_next_command_and_inputs(retries = RETRY_COUNT, error_outcome = nil) + def determine_next_command_and_inputs return if killed - if verbose? && retries != RETRY_COUNT - # :nocov: - (io_err || $stderr).puts " !!! Retrying to determine next command and inputs. Retries left: #{retries}" - # :nocov: - end - - if retries == 0 - # TODO: test this path by irreparably breaking the needed commands - # :nocov: - self.next_command_name = GiveUp.full_command_name - self.next_command_inputs = { - message_to_user: "While trying to choose the next command and inputs, " \ - "I've ran into an error several times that I couldn't figure out how to get past. " \ - "The last error looked like this:\n#{error_outcome.errors_hash}" - } - self.next_command_raw_inputs = next_command_inputs - - return - # :nocov: - end - compact_command_log inputs_for_determine = { agent:, llm_model: } @@ -175,33 +153,6 @@ def determine_next_command_and_inputs(retries = RETRY_COUNT, error_outcome = nil if outcome.success? fetch_next_command_class - - if need_to_describe_next_command? - simulate_describe_command - return determine_next_command_and_inputs(retries, outcome) - end - - if next_command_has_inputs? - outcome = validate_next_command_inputs - - unless outcome.success? - # TODO: test this path - # :nocov: - log_command_outcome( - command_name: next_command_name, - inputs: next_command_inputs, - outcome: - ) - - simulate_describe_command - - determine_next_command_and_inputs(retries - 1, outcome) - # :nocov: - end - else - self.next_command_inputs = {} - self.next_command_raw_inputs = next_command_inputs - end else log_command_outcome( command_name: next_command_name, @@ -210,7 +161,9 @@ def determine_next_command_and_inputs(retries = RETRY_COUNT, error_outcome = nil result: nil ) - determine_next_command_and_inputs(retries - 1, outcome) + self.error_outcome = outcome + increment_and_check_retry_count + determine_next_command_and_inputs end else log_command_outcome( @@ -220,7 +173,9 @@ def determine_next_command_and_inputs(retries = RETRY_COUNT, error_outcome = nil result: outcome.result || determine_command.raw_result ) - determine_next_command_and_inputs(retries - 1, outcome) + self.error_outcome = outcome + increment_and_check_retry_count + determine_next_command_and_inputs end end @@ -248,14 +203,41 @@ def validate_next_command_name end def validate_next_command_inputs - inputs_type = next_command_class.inputs_type + if next_command_has_inputs? + inputs_type = next_command_class.inputs_type + + outcome = NestedTransactionable.with_needed_transactions_for_type(inputs_type) do + inputs = next_command_inputs.nil? ? {} : next_command_inputs + inputs_type.process_value(inputs) + end + + valid = outcome.success? + self.command_inputs_valid = valid - NestedTransactionable.with_needed_transactions_for_type(inputs_type) do - inputs = next_command_inputs.nil? ? {} : next_command_inputs - inputs_type.process_value(inputs) + if valid + self.next_command_inputs = outcome.result + # Do we really need this raw inputs variable? + self.next_command_raw_inputs = next_command_inputs + else # TODO: test this path + # :nocov: + log_command_outcome( + command_name: next_command_name, + inputs: next_command_inputs, + outcome: + ) + # :nocov: + end + else + self.command_inputs_valid = true + self.next_command_inputs = {} + self.next_command_raw_inputs = next_command_inputs end end + def command_inputs_valid? + command_inputs_valid + end + def command_name_type @command_name_type ||= Agent.foobara_type_from_declaration(:string, one_of: all_command_classes) end @@ -274,6 +256,9 @@ def next_command_has_inputs? end def run_next_command + increment_command_calls + reset_retry_count + log_command_code(command_name: next_command_name, inputs: next_command_inputs) self.command_response = agent.run( @@ -348,6 +333,31 @@ def compact_command_log context.command_log = new_log end + MAX_RETRY_COUNT = 3 + + def increment_and_check_retry_count + self.retry_count ||= 0 + self.retry_count += 1 + + if retry_count > MAX_RETRY_COUNT + # TODO: test this path by irreparably breaking the needed commands + # :nocov: + self.next_command_name = GiveUp.full_command_name + self.next_command_inputs = { + message_to_user: "While trying to choose the next command and inputs, " \ + "I've ran into an error several times that I couldn't figure out how to get past. " \ + "The last error looked like this:\n#{error_outcome.errors_hash}" + } + self.next_command_raw_inputs = next_command_inputs + # :nocov: + elsif verbose? + # :nocov: + (io_err || $stderr).puts " !!! Retrying to determine next command and inputs. " \ + "Retries left: #{RETRY_COUNT - retry_count}" + # :nocov: + end + end + def increment_command_calls self.command_calls ||= -1 self.command_calls += 1