roast-ai 0.4.7 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -0
- data/Gemfile.lock +3 -3
- data/README.md +9 -5
- data/Rakefile +2 -0
- data/dsl/less_simple.rb +112 -0
- data/dsl/prototype.rb +17 -0
- data/dsl/simple.rb +5 -7
- data/dsl/step_communication.rb +18 -0
- data/examples/README.md +9 -0
- data/examples/available_tools_demo/workflow.yml +1 -1
- data/examples/basic_prompt_workflow/workflow.md +1 -0
- data/examples/basic_prompt_workflow/workflow.yml +14 -0
- data/examples/grading/README.md +1 -26
- data/examples/grading/analyze_coverage/prompt.md +1 -1
- data/examples/grading/calculate_final_grade.rb +10 -13
- data/examples/grading/format_result.rb +5 -8
- data/examples/grading/generate_grades/prompt.md +1 -1
- data/examples/grading/generate_recommendations/prompt.md +1 -1
- data/examples/grading/read_dependencies/prompt.md +0 -1
- data/examples/grading/verify_test_helpers/prompt.md +1 -1
- data/examples/grading/workflow.md +1 -4
- data/examples/grading/workflow.yml +3 -16
- data/lib/roast/dsl/cog/config.rb +31 -0
- data/lib/roast/dsl/cog/stack.rb +21 -0
- data/lib/roast/dsl/cog/store.rb +26 -0
- data/lib/roast/dsl/cog.rb +70 -0
- data/lib/roast/dsl/cog_execution_context.rb +29 -0
- data/lib/roast/dsl/cogs/cmd.rb +55 -0
- data/lib/roast/dsl/cogs/graph.rb +53 -0
- data/lib/roast/dsl/cogs.rb +65 -0
- data/lib/roast/dsl/config_context.rb +54 -0
- data/lib/roast/dsl/executor.rb +62 -7
- data/lib/roast/dsl/workflow_execution_context.rb +47 -0
- data/lib/roast/error.rb +7 -0
- data/lib/roast/errors.rb +3 -3
- data/lib/roast/graph/edge.rb +25 -0
- data/lib/roast/graph/node.rb +40 -0
- data/lib/roast/graph/quantum_edge.rb +27 -0
- data/lib/roast/graph/threaded_exec.rb +93 -0
- data/lib/roast/graph.rb +233 -0
- data/lib/roast/resources/api_resource.rb +2 -2
- data/lib/roast/resources/url_resource.rb +2 -2
- data/lib/roast/tools/apply_diff.rb +1 -1
- data/lib/roast/tools/ask_user.rb +1 -1
- data/lib/roast/tools/bash.rb +1 -1
- data/lib/roast/tools/cmd.rb +2 -2
- data/lib/roast/tools/coding_agent.rb +2 -2
- data/lib/roast/tools/grep.rb +1 -1
- data/lib/roast/tools/read_file.rb +1 -1
- data/lib/roast/tools/search_file.rb +1 -1
- data/lib/roast/tools/swarm.rb +1 -1
- data/lib/roast/tools/update_files.rb +2 -2
- data/lib/roast/tools/write_file.rb +1 -1
- data/lib/roast/tools.rb +1 -1
- data/lib/roast/value_objects/api_token.rb +1 -1
- data/lib/roast/value_objects/uri_base.rb +1 -1
- data/lib/roast/value_objects/workflow_path.rb +1 -1
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/base_step.rb +2 -3
- data/lib/roast/workflow/base_workflow.rb +38 -2
- data/lib/roast/workflow/command_executor.rb +1 -1
- data/lib/roast/workflow/configuration_loader.rb +1 -1
- data/lib/roast/workflow/error_handler.rb +1 -1
- data/lib/roast/workflow/step_executor_registry.rb +1 -1
- data/lib/roast/workflow/step_loader.rb +3 -8
- data/lib/roast/workflow/workflow_executor.rb +1 -1
- data/lib/roast.rb +7 -2
- data/sorbet/config +2 -0
- data/sorbet/rbi/annotations/.gitattributes +1 -0
- data/sorbet/rbi/annotations/activesupport.rbi +495 -0
- data/sorbet/rbi/annotations/faraday.rbi +17 -0
- data/sorbet/rbi/annotations/minitest.rbi +119 -0
- data/sorbet/rbi/annotations/mocha.rbi +34 -0
- data/sorbet/rbi/annotations/rainbow.rbi +269 -0
- data/sorbet/rbi/annotations/webmock.rbi +9 -0
- data/sorbet/rbi/gems/rbs-inline@0.12.0.rbi +2170 -0
- data/sorbet/rbi/gems/{rexml@3.4.1.rbi → rexml@3.4.2.rbi} +284 -239
- data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +11 -0
- data/sorbet/rbi/shims/lib/roast/dsl/workflow_execution_context.rbi +11 -0
- data/sorbet/rbi/todo.rbi +7 -0
- metadata +37 -231
- data/CHANGELOG.md +0 -369
- data/examples/agent_continue/add_documentation/prompt.md +0 -5
- data/examples/agent_continue/add_error_handling/prompt.md +0 -5
- data/examples/agent_continue/analyze_codebase/prompt.md +0 -7
- data/examples/agent_continue/combined_workflow.yml +0 -24
- data/examples/agent_continue/continue_adding_features/prompt.md +0 -4
- data/examples/agent_continue/create_integration_tests/prompt.md +0 -3
- data/examples/agent_continue/document_with_context/prompt.md +0 -5
- data/examples/agent_continue/explore_api/prompt.md +0 -6
- data/examples/agent_continue/implement_client/prompt.md +0 -6
- data/examples/agent_continue/inline_workflow.yml +0 -20
- data/examples/agent_continue/refactor_code/prompt.md +0 -2
- data/examples/agent_continue/verify_changes/prompt.md +0 -6
- data/examples/agent_continue/workflow.yml +0 -27
- data/examples/agent_workflow/README.md +0 -75
- data/examples/agent_workflow/apply_refactorings/prompt.md +0 -22
- data/examples/agent_workflow/identify_code_smells/prompt.md +0 -15
- data/examples/agent_workflow/summarize_improvements/prompt.md +0 -18
- data/examples/agent_workflow/workflow.png +0 -0
- data/examples/agent_workflow/workflow.yml +0 -16
- data/examples/api_workflow/README.md +0 -85
- data/examples/api_workflow/fetch_api_data/prompt.md +0 -10
- data/examples/api_workflow/generate_report/prompt.md +0 -10
- data/examples/api_workflow/prompt.md +0 -10
- data/examples/api_workflow/transform_data/prompt.md +0 -10
- data/examples/api_workflow/workflow.png +0 -0
- data/examples/api_workflow/workflow.yml +0 -30
- data/examples/apply_diff_demo/README.md +0 -58
- data/examples/apply_diff_demo/apply_simple_change/prompt.md +0 -13
- data/examples/apply_diff_demo/create_sample_file/prompt.md +0 -11
- data/examples/apply_diff_demo/workflow.yml +0 -24
- data/examples/available_tools_demo/workflow.png +0 -0
- data/examples/bash_prototyping/README.md +0 -53
- data/examples/bash_prototyping/analyze_network/prompt.md +0 -13
- data/examples/bash_prototyping/analyze_system/prompt.md +0 -11
- data/examples/bash_prototyping/api_testing.png +0 -0
- data/examples/bash_prototyping/api_testing.yml +0 -14
- data/examples/bash_prototyping/check_processes/prompt.md +0 -11
- data/examples/bash_prototyping/generate_report/prompt.md +0 -16
- data/examples/bash_prototyping/process_json_response/prompt.md +0 -24
- data/examples/bash_prototyping/system_analysis.png +0 -0
- data/examples/bash_prototyping/system_analysis.yml +0 -14
- data/examples/bash_prototyping/test_public_api/prompt.md +0 -22
- data/examples/case_when/README.md +0 -58
- data/examples/case_when/detect_language/prompt.md +0 -16
- data/examples/case_when/workflow.png +0 -0
- data/examples/case_when/workflow.yml +0 -58
- data/examples/cmd/README.md +0 -99
- data/examples/cmd/analyze_project/prompt.md +0 -57
- data/examples/cmd/basic_demo/prompt.md +0 -48
- data/examples/cmd/basic_workflow.png +0 -0
- data/examples/cmd/basic_workflow.yml +0 -16
- data/examples/cmd/check_repository/prompt.md +0 -57
- data/examples/cmd/create_and_verify/prompt.md +0 -56
- data/examples/cmd/dev_workflow.png +0 -0
- data/examples/cmd/dev_workflow.yml +0 -26
- data/examples/cmd/explore_project/prompt.md +0 -67
- data/examples/cmd/explorer_workflow.png +0 -0
- data/examples/cmd/explorer_workflow.yml +0 -21
- data/examples/cmd/smart_tool_selection/prompt.md +0 -99
- data/examples/coding_agent_with_model.yml +0 -20
- data/examples/coding_agent_with_retries.yml +0 -30
- data/examples/conditional/README.md +0 -161
- data/examples/conditional/check_condition/prompt.md +0 -1
- data/examples/conditional/simple_workflow.png +0 -0
- data/examples/conditional/simple_workflow.yml +0 -15
- data/examples/conditional/workflow.png +0 -0
- data/examples/conditional/workflow.yml +0 -23
- data/examples/context_management_demo/README.md +0 -43
- data/examples/context_management_demo/workflow.yml +0 -42
- data/examples/direct_coerce_syntax/README.md +0 -32
- data/examples/direct_coerce_syntax/workflow.png +0 -0
- data/examples/direct_coerce_syntax/workflow.yml +0 -36
- data/examples/dot_notation/README.md +0 -37
- data/examples/dot_notation/workflow.png +0 -0
- data/examples/dot_notation/workflow.yml +0 -44
- data/examples/exit_on_error/README.md +0 -50
- data/examples/exit_on_error/analyze_lint_output/prompt.md +0 -9
- data/examples/exit_on_error/apply_fixes/prompt.md +0 -2
- data/examples/exit_on_error/workflow.png +0 -0
- data/examples/exit_on_error/workflow.yml +0 -19
- data/examples/grading/js_test_runner +0 -31
- data/examples/grading/rb_test_runner +0 -19
- data/examples/grading/run_coverage.rb +0 -54
- data/examples/grading/workflow.png +0 -0
- data/examples/grading/workflow.rb.md +0 -6
- data/examples/grading/workflow.ts+tsx.md +0 -6
- data/examples/instrumentation.rb +0 -76
- data/examples/interpolation/README.md +0 -50
- data/examples/interpolation/analyze_file/prompt.md +0 -1
- data/examples/interpolation/analyze_patterns/prompt.md +0 -27
- data/examples/interpolation/generate_report_for_js/prompt.md +0 -3
- data/examples/interpolation/generate_report_for_rb/prompt.md +0 -3
- data/examples/interpolation/sample.js +0 -48
- data/examples/interpolation/sample.rb +0 -42
- data/examples/interpolation/workflow.md +0 -1
- data/examples/interpolation/workflow.png +0 -0
- data/examples/interpolation/workflow.yml +0 -21
- data/examples/iteration/IMPLEMENTATION.md +0 -88
- data/examples/iteration/README.md +0 -68
- data/examples/iteration/analyze_complexity/prompt.md +0 -22
- data/examples/iteration/generate_recommendations/prompt.md +0 -21
- data/examples/iteration/generate_report/prompt.md +0 -129
- data/examples/iteration/implement_fix/prompt.md +0 -25
- data/examples/iteration/prioritize_issues/prompt.md +0 -24
- data/examples/iteration/prompts/analyze_file.md +0 -28
- data/examples/iteration/prompts/generate_summary.md +0 -24
- data/examples/iteration/prompts/update_report.md +0 -29
- data/examples/iteration/prompts/write_report.md +0 -22
- data/examples/iteration/read_file/prompt.md +0 -9
- data/examples/iteration/select_next_issue/prompt.md +0 -25
- data/examples/iteration/simple_workflow.md +0 -39
- data/examples/iteration/simple_workflow.yml +0 -58
- data/examples/iteration/update_fix_count/prompt.md +0 -26
- data/examples/iteration/verify_fix/prompt.md +0 -29
- data/examples/iteration/workflow.png +0 -0
- data/examples/iteration/workflow.yml +0 -42
- data/examples/json_handling/README.md +0 -32
- data/examples/json_handling/workflow.png +0 -0
- data/examples/json_handling/workflow.yml +0 -52
- data/examples/mcp/README.md +0 -223
- data/examples/mcp/analyze_changes/prompt.md +0 -8
- data/examples/mcp/analyze_issues/prompt.md +0 -4
- data/examples/mcp/analyze_schema/prompt.md +0 -4
- data/examples/mcp/check_data_quality/prompt.md +0 -5
- data/examples/mcp/check_documentation/prompt.md +0 -4
- data/examples/mcp/create_recommendations/prompt.md +0 -5
- data/examples/mcp/database_workflow.png +0 -0
- data/examples/mcp/database_workflow.yml +0 -29
- data/examples/mcp/env_demo/workflow.png +0 -0
- data/examples/mcp/env_demo/workflow.yml +0 -34
- data/examples/mcp/fetch_pr_context/prompt.md +0 -4
- data/examples/mcp/filesystem_demo/create_test_file/prompt.md +0 -2
- data/examples/mcp/filesystem_demo/list_files/prompt.md +0 -6
- data/examples/mcp/filesystem_demo/read_with_mcp/prompt.md +0 -7
- data/examples/mcp/filesystem_demo/workflow.png +0 -0
- data/examples/mcp/filesystem_demo/workflow.yml +0 -38
- data/examples/mcp/generate_insights/prompt.md +0 -4
- data/examples/mcp/generate_report/prompt.md +0 -6
- data/examples/mcp/generate_review/prompt.md +0 -16
- data/examples/mcp/github_workflow.png +0 -0
- data/examples/mcp/github_workflow.yml +0 -32
- data/examples/mcp/multi_mcp_workflow.png +0 -0
- data/examples/mcp/multi_mcp_workflow.yml +0 -58
- data/examples/mcp/post_review/prompt.md +0 -3
- data/examples/mcp/save_report/prompt.md +0 -6
- data/examples/mcp/search_issues/prompt.md +0 -2
- data/examples/mcp/summarize/prompt.md +0 -1
- data/examples/mcp/test_filesystem/prompt.md +0 -6
- data/examples/mcp/test_github/prompt.md +0 -8
- data/examples/mcp/test_read/prompt.md +0 -1
- data/examples/mcp/workflow.png +0 -0
- data/examples/mcp/workflow.yml +0 -35
- data/examples/no_model_fallback/README.md +0 -17
- data/examples/no_model_fallback/analyze_file/prompt.md +0 -1
- data/examples/no_model_fallback/analyze_patterns/prompt.md +0 -27
- data/examples/no_model_fallback/generate_report_for_md/prompt.md +0 -10
- data/examples/no_model_fallback/generate_report_for_rb/prompt.md +0 -3
- data/examples/no_model_fallback/sample.rb +0 -42
- data/examples/no_model_fallback/workflow.yml +0 -19
- data/examples/openrouter_example/README.md +0 -48
- data/examples/openrouter_example/analyze_input/prompt.md +0 -16
- data/examples/openrouter_example/generate_response/prompt.md +0 -9
- data/examples/openrouter_example/workflow.png +0 -0
- data/examples/openrouter_example/workflow.yml +0 -12
- data/examples/pre_post_processing/README.md +0 -111
- data/examples/pre_post_processing/analyze_test_file/prompt.md +0 -23
- data/examples/pre_post_processing/improve_test_coverage/prompt.md +0 -17
- data/examples/pre_post_processing/optimize_test_performance/prompt.md +0 -25
- data/examples/pre_post_processing/post_processing/aggregate_metrics/prompt.md +0 -31
- data/examples/pre_post_processing/post_processing/cleanup_environment/prompt.md +0 -28
- data/examples/pre_post_processing/post_processing/generate_summary_report/prompt.md +0 -32
- data/examples/pre_post_processing/post_processing/output.txt +0 -24
- data/examples/pre_post_processing/pre_processing/gather_baseline_metrics/prompt.md +0 -26
- data/examples/pre_post_processing/pre_processing/setup_test_environment/prompt.md +0 -11
- data/examples/pre_post_processing/validate_changes/prompt.md +0 -24
- data/examples/pre_post_processing/workflow.png +0 -0
- data/examples/pre_post_processing/workflow.yml +0 -21
- data/examples/retry/workflow.yml +0 -23
- data/examples/rspec_to_minitest/README.md +0 -68
- data/examples/rspec_to_minitest/analyze_spec/prompt.md +0 -30
- data/examples/rspec_to_minitest/create_minitest/prompt.md +0 -33
- data/examples/rspec_to_minitest/run_and_improve/prompt.md +0 -35
- data/examples/rspec_to_minitest/workflow.md +0 -10
- data/examples/rspec_to_minitest/workflow.png +0 -0
- data/examples/rspec_to_minitest/workflow.yml +0 -40
- data/examples/shared_config/README.md +0 -52
- data/examples/shared_config/example_with_shared_config/workflow.png +0 -0
- data/examples/shared_config/example_with_shared_config/workflow.yml +0 -6
- data/examples/shared_config/shared.png +0 -0
- data/examples/shared_config/shared.yml +0 -7
- data/examples/single_target_prepost/README.md +0 -36
- data/examples/single_target_prepost/post_processing/output.txt +0 -27
- data/examples/single_target_prepost/pre_processing/gather_dependencies/prompt.md +0 -11
- data/examples/single_target_prepost/workflow.png +0 -0
- data/examples/single_target_prepost/workflow.yml +0 -20
- data/examples/smart_coercion_defaults/README.md +0 -65
- data/examples/smart_coercion_defaults/workflow.png +0 -0
- data/examples/smart_coercion_defaults/workflow.yml +0 -44
- data/examples/step_configuration/README.md +0 -84
- data/examples/step_configuration/workflow.png +0 -0
- data/examples/step_configuration/workflow.yml +0 -57
- data/examples/swarm_example.yml +0 -25
- data/examples/tool_config_example/README.md +0 -109
- data/examples/tool_config_example/example_step/prompt.md +0 -42
- data/examples/tool_config_example/workflow.png +0 -0
- data/examples/tool_config_example/workflow.yml +0 -17
- data/examples/user_input/README.md +0 -90
- data/examples/user_input/funny_name/create_backstory/prompt.md +0 -10
- data/examples/user_input/funny_name/workflow.png +0 -0
- data/examples/user_input/funny_name/workflow.yml +0 -25
- data/examples/user_input/generate_summary/prompt.md +0 -11
- data/examples/user_input/simple_input_demo/workflow.png +0 -0
- data/examples/user_input/simple_input_demo/workflow.yml +0 -35
- data/examples/user_input/survey_workflow.png +0 -0
- data/examples/user_input/survey_workflow.yml +0 -71
- data/examples/user_input/welcome_message/prompt.md +0 -3
- data/examples/user_input/workflow.png +0 -0
- data/examples/user_input/workflow.yml +0 -73
- data/examples/workflow_generator/README.md +0 -27
- data/examples/workflow_generator/analyze_user_request/prompt.md +0 -34
- data/examples/workflow_generator/create_workflow_files/prompt.md +0 -32
- data/examples/workflow_generator/get_user_input/prompt.md +0 -14
- data/examples/workflow_generator/info_from_roast.rb +0 -22
- data/examples/workflow_generator/workflow.png +0 -0
- data/examples/workflow_generator/workflow.yml +0 -34
- data/package-lock.json +0 -6
- /data/sorbet/rbi/gems/{rack@2.2.17.rbi → rack@2.2.18.rbi} +0 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
module DSL
|
6
|
+
# Contains the cogs already executed in this run.
|
7
|
+
class CogExecutionContext
|
8
|
+
# Raises if you access a cog in an execution block that hasn't already been run.
|
9
|
+
class IncompleteCogExecutionAccessError < StandardError; end
|
10
|
+
|
11
|
+
def initialize(cogs, bound_names)
|
12
|
+
@cogs = cogs
|
13
|
+
bind_cog_methods(bound_names)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def bind_cog_methods(bound_names)
|
19
|
+
bound_names.map do |name|
|
20
|
+
define_singleton_method(name.to_sym, ->(name) do
|
21
|
+
@cogs[name].tap do |cog|
|
22
|
+
raise IncompleteCogExecutionAccessError unless cog.ran?
|
23
|
+
end.output
|
24
|
+
end)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
module DSL
|
6
|
+
module Cogs
|
7
|
+
class Cmd < Cog
|
8
|
+
class Output
|
9
|
+
#: String?
|
10
|
+
attr_reader :command_output
|
11
|
+
|
12
|
+
#: String?
|
13
|
+
attr_reader :err
|
14
|
+
|
15
|
+
#: Process::Status?
|
16
|
+
attr_reader :status
|
17
|
+
|
18
|
+
#: (
|
19
|
+
#| String? output,
|
20
|
+
#| String? error,
|
21
|
+
#| Process::Status? status
|
22
|
+
#| ) -> void
|
23
|
+
def initialize(output, error, status)
|
24
|
+
@command_output = output
|
25
|
+
@err = error
|
26
|
+
@status = status
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Config < Cog::Config
|
31
|
+
#: () -> void
|
32
|
+
def print_all!
|
33
|
+
@values[:print_all] = true
|
34
|
+
end
|
35
|
+
|
36
|
+
#: () -> bool
|
37
|
+
def print_all?
|
38
|
+
!!@values[:print_all]
|
39
|
+
end
|
40
|
+
|
41
|
+
def display!
|
42
|
+
print_all!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
#: (String) -> Output
|
47
|
+
def execute(input)
|
48
|
+
result = Output.new(*Roast::Helpers::CmdRunner.capture3(input))
|
49
|
+
puts result.command_output if @config.print_all?
|
50
|
+
result
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
module DSL
|
6
|
+
module Cogs
|
7
|
+
class Graph < Roast::DSL::Cog
|
8
|
+
#: Proc
|
9
|
+
attr_reader :block
|
10
|
+
|
11
|
+
#: (Symbol) { (Roast::DSL::Cogs::Graph) -> void } -> void
|
12
|
+
def initialize(name, &block)
|
13
|
+
@name = name
|
14
|
+
@block = block
|
15
|
+
@graph = Roast::Graph.new
|
16
|
+
super(name, proc {})
|
17
|
+
end
|
18
|
+
|
19
|
+
#: () -> void
|
20
|
+
def on_invoke
|
21
|
+
populate!(@graph)
|
22
|
+
end
|
23
|
+
|
24
|
+
#: () -> Symbol
|
25
|
+
def store_id
|
26
|
+
@name.to_sym
|
27
|
+
end
|
28
|
+
|
29
|
+
#: (Roast::DSL::Cog) -> void
|
30
|
+
def update(other)
|
31
|
+
return unless other.is_a?(Roast::DSL::Cogs::Graph)
|
32
|
+
|
33
|
+
return if other.block.nil?
|
34
|
+
|
35
|
+
other.populate!(@graph)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Populates the provided graph in-place, with the definition of how to populate in the block
|
39
|
+
#: (Roast::DSL::Cogs::Graph) -> void
|
40
|
+
def populate!(graph)
|
41
|
+
return if @block.nil?
|
42
|
+
|
43
|
+
@block.call(graph)
|
44
|
+
end
|
45
|
+
|
46
|
+
#: () -> void
|
47
|
+
def execute
|
48
|
+
@graph.execute
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
module DSL
|
6
|
+
module Cogs
|
7
|
+
class << self
|
8
|
+
#: (String) -> void
|
9
|
+
def load_all_for(rube_rb_fpath)
|
10
|
+
rube_rb_fpath = File.expand_path(rube_rb_fpath)
|
11
|
+
all_cog_files(rube_rb_fpath).each do |cog_fpath|
|
12
|
+
require cog_fpath
|
13
|
+
end
|
14
|
+
bind_all_cog_invokers
|
15
|
+
end
|
16
|
+
|
17
|
+
#: () -> Array[Class]
|
18
|
+
def all_cog_classes
|
19
|
+
# rubocop:disable Sorbet/ConstantsFromStrings
|
20
|
+
Roast::DSL::Cogs.constants.map do |cog_class_name|
|
21
|
+
Roast::DSL::Cogs.const_get(cog_class_name)
|
22
|
+
end
|
23
|
+
# rubocop:enable Sorbet/ConstantsFromStrings
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
#: () -> void
|
29
|
+
def bind_all_cog_invokers
|
30
|
+
all_cog_classes.each do |cog_class|
|
31
|
+
# At some point we may want to tuck this all into a Roast::DSL::Binding/Scope/Context to avoid polluting toplevel.
|
32
|
+
TOPLEVEL_BINDING.eval(binding_string_for(cog_class))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#: (Class) -> String
|
37
|
+
def binding_string_for(cog_class)
|
38
|
+
<<~RUBY
|
39
|
+
def #{cog_class.method_name}(*args, **kwargs, &block)
|
40
|
+
#{cog_class.name}.invoke(*args, **kwargs, &block)
|
41
|
+
end
|
42
|
+
RUBY
|
43
|
+
end
|
44
|
+
|
45
|
+
#: (String) -> Array[String]
|
46
|
+
def all_cog_files(rube_rb_fpath)
|
47
|
+
dirs = [project_cogs_dir(rube_rb_fpath), internal_cogs_dir]
|
48
|
+
dirs.map do |dir|
|
49
|
+
Dir.glob(File.join(dir, "*.rb")) # Just toplevel .rb files
|
50
|
+
end.flatten
|
51
|
+
end
|
52
|
+
|
53
|
+
#: (String) -> String
|
54
|
+
def project_cogs_dir(rube_rb_fpath)
|
55
|
+
File.join(File.dirname(rube_rb_fpath), "cogs")
|
56
|
+
end
|
57
|
+
|
58
|
+
#: () -> String
|
59
|
+
def internal_cogs_dir
|
60
|
+
File.join(File.dirname(__FILE__), "cogs")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
module DSL
|
6
|
+
class ConfigContext
|
7
|
+
def initialize(cogs, config_proc)
|
8
|
+
@cogs = cogs
|
9
|
+
@executor_scoped_configs = {}
|
10
|
+
@cog_scoped_configs = {}
|
11
|
+
@config_proc = config_proc
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch_merged_config(cog_class, name = nil)
|
15
|
+
# All configs have an entry, even if it's empty.
|
16
|
+
configs = fetch_execution_scope(cog_class)
|
17
|
+
instance_configs = fetch_cog_config(cog_class, name) unless name.nil?
|
18
|
+
configs = configs.merge(instance_configs) if instance_configs
|
19
|
+
configs
|
20
|
+
end
|
21
|
+
|
22
|
+
def prepare!
|
23
|
+
bind_default_cogs
|
24
|
+
instance_eval(&@config_proc)
|
25
|
+
end
|
26
|
+
|
27
|
+
#: () -> void
|
28
|
+
def bind_default_cogs
|
29
|
+
bind_cog(Cogs::Cmd, :cmd)
|
30
|
+
end
|
31
|
+
|
32
|
+
def fetch_cog_config(cog_class, name)
|
33
|
+
@cog_scoped_configs[cog_class][name]
|
34
|
+
end
|
35
|
+
|
36
|
+
def fetch_or_create_cog_config(cog_class, name)
|
37
|
+
@cog_scoped_configs[cog_class][name] = cog_class.config_class.new unless @cog_scoped_configs.key?(name)
|
38
|
+
@cog_scoped_configs[cog_class][name]
|
39
|
+
end
|
40
|
+
|
41
|
+
def fetch_execution_scope(cog_class)
|
42
|
+
@executor_scoped_configs[cog_class]
|
43
|
+
end
|
44
|
+
|
45
|
+
def bind_cog(cog_class, method_name)
|
46
|
+
@cog_scoped_configs[cog_class] = {}
|
47
|
+
@executor_scoped_configs[cog_class] = cog_class.config_class.new
|
48
|
+
instance_eval do
|
49
|
+
define_singleton_method(method_name, &cog_class.on_config)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/roast/dsl/executor.rb
CHANGED
@@ -4,23 +4,78 @@
|
|
4
4
|
module Roast
|
5
5
|
module DSL
|
6
6
|
class Executor
|
7
|
+
class ExecutorError < Roast::Error; end
|
8
|
+
class ExecutorAlreadyPreparedError < ExecutorError; end
|
9
|
+
class ExecutorAlreadyCompletedError < ExecutorError; end
|
10
|
+
|
7
11
|
class << self
|
8
12
|
def from_file(workflow_path)
|
9
|
-
|
13
|
+
run!(File.read(workflow_path))
|
10
14
|
end
|
11
15
|
|
12
16
|
private
|
13
17
|
|
14
|
-
def
|
15
|
-
new
|
18
|
+
def run!(workflow_definition)
|
19
|
+
executor = new
|
20
|
+
executor.prepare!(workflow_definition)
|
21
|
+
executor.start!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def prepare!(input)
|
26
|
+
# You can only initialize an executor once.
|
27
|
+
raise ExecutorAlreadyPreparedError if @prepared
|
28
|
+
|
29
|
+
extract_dsl_procs(input)
|
30
|
+
@cogs = Cog::Store.new
|
31
|
+
@cog_stack = Cog::Stack.new
|
32
|
+
|
33
|
+
@config_context = ConfigContext.new(@cogs, @config_proc)
|
34
|
+
@config_context.prepare!
|
35
|
+
@execution_context = WorkflowExecutionContext.new(@cogs, @cog_stack, @execution_proc)
|
36
|
+
@execution_context.prepare!
|
37
|
+
|
38
|
+
@prepared = true
|
39
|
+
end
|
40
|
+
|
41
|
+
def start!
|
42
|
+
# Now we run the cogs!
|
43
|
+
# You can only do this once, executors are not reusable to avoid state pollution
|
44
|
+
raise ExecutorAlreadyCompletedError if @completed
|
45
|
+
|
46
|
+
@cog_stack.map do |name, cog|
|
47
|
+
cog.run!(
|
48
|
+
@config_context.fetch_merged_config(cog.class, name.to_sym),
|
49
|
+
@execution_context.cog_execution_context,
|
50
|
+
)
|
16
51
|
end
|
52
|
+
|
53
|
+
@completed = true
|
17
54
|
end
|
18
55
|
|
19
|
-
|
56
|
+
def prepared?
|
57
|
+
@prepared ||= false
|
58
|
+
end
|
59
|
+
|
60
|
+
def completed?
|
61
|
+
@completed ||= false
|
62
|
+
end
|
63
|
+
|
64
|
+
#: { () [self: ConfigContext] -> void} -> void
|
65
|
+
def config(&block)
|
66
|
+
@config_proc = block
|
67
|
+
end
|
68
|
+
|
69
|
+
#: { () [self: WorkflowExecutionContext] -> void} -> void
|
70
|
+
def execute(&block)
|
71
|
+
@execution_proc = block
|
72
|
+
end
|
20
73
|
|
21
|
-
|
22
|
-
|
23
|
-
|
74
|
+
# Separating the instance evals ensures that we can reuse the same cog method
|
75
|
+
# names between config and execute, while have the backing objects be completely
|
76
|
+
# different. This means we have an enforced separation between configuring and running.
|
77
|
+
def extract_dsl_procs(input)
|
78
|
+
instance_eval(input)
|
24
79
|
end
|
25
80
|
end
|
26
81
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
module DSL
|
6
|
+
class WorkflowExecutionContext
|
7
|
+
def initialize(cogs, cog_stack, execution_proc)
|
8
|
+
@cogs = cogs
|
9
|
+
@cog_stack = cog_stack
|
10
|
+
@execution_proc = execution_proc
|
11
|
+
@bound_names = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def prepare!
|
15
|
+
bind_default_cogs
|
16
|
+
instance_eval(&@execution_proc)
|
17
|
+
end
|
18
|
+
|
19
|
+
def cog_execution_context
|
20
|
+
@cog_execution_context ||= CogExecutionContext.new(@cogs, @bound_names)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def add_cog_instance(name, cog)
|
26
|
+
@cogs.insert(name, cog)
|
27
|
+
@cog_stack.push([name, cog])
|
28
|
+
end
|
29
|
+
|
30
|
+
def output(name)
|
31
|
+
@cogs[name].output
|
32
|
+
end
|
33
|
+
|
34
|
+
#: () -> void
|
35
|
+
def bind_default_cogs
|
36
|
+
bind_cog(Cogs::Cmd, :cmd)
|
37
|
+
end
|
38
|
+
|
39
|
+
def bind_cog(cog_class, name)
|
40
|
+
@bound_names << name
|
41
|
+
instance_eval do
|
42
|
+
define_singleton_method(name, &cog_class.on_create)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/roast/error.rb
ADDED
data/lib/roast/errors.rb
CHANGED
@@ -4,12 +4,12 @@
|
|
4
4
|
module Roast
|
5
5
|
module Errors
|
6
6
|
# Custom error for API resource not found (404) responses
|
7
|
-
class ResourceNotFoundError <
|
7
|
+
class ResourceNotFoundError < Roast::Error; end
|
8
8
|
|
9
9
|
# Custom error for when API authentication fails
|
10
|
-
class AuthenticationError <
|
10
|
+
class AuthenticationError < Roast::Error; end
|
11
11
|
|
12
12
|
# Exit the app, for instance via Ctrl-C during an InputStep
|
13
|
-
class ExitEarly <
|
13
|
+
class ExitEarly < Roast::Error; end
|
14
14
|
end
|
15
15
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
class Graph
|
6
|
+
class Edge
|
7
|
+
#: (Node) -> void
|
8
|
+
attr_writer :join_node
|
9
|
+
|
10
|
+
attr_reader :from_node, :to_node
|
11
|
+
|
12
|
+
#: (Node, Node, ?proc: Proc?) -> void
|
13
|
+
def initialize(from_node, to_node, proc: nil)
|
14
|
+
@from_node = from_node
|
15
|
+
@to_node = to_node
|
16
|
+
@proc = proc # TODO: Shadowing proc builtin here
|
17
|
+
end
|
18
|
+
|
19
|
+
#: () -> String
|
20
|
+
def to_s
|
21
|
+
"#{@from_node} -> #{@to_node}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
class Graph
|
6
|
+
class Node
|
7
|
+
# class InvalidExecutableError < Roast::Error; end
|
8
|
+
|
9
|
+
attr_reader :name, :executable
|
10
|
+
|
11
|
+
#: (Symbol, ?executable: Proc | Graph | nil) -> void
|
12
|
+
def initialize(name, executable: nil)
|
13
|
+
@name = name
|
14
|
+
@executable = executable
|
15
|
+
end
|
16
|
+
|
17
|
+
#: () -> T::Boolean
|
18
|
+
def subgraph?
|
19
|
+
@executable.is_a?(Graph)
|
20
|
+
end
|
21
|
+
|
22
|
+
#: () -> T::Boolean
|
23
|
+
def done?
|
24
|
+
@name == :DONE
|
25
|
+
end
|
26
|
+
|
27
|
+
#: (Hash) -> void
|
28
|
+
def execute(state)
|
29
|
+
return if @executable.nil?
|
30
|
+
|
31
|
+
executable = @executable
|
32
|
+
if executable.is_a?(Proc)
|
33
|
+
executable.call(state)
|
34
|
+
elsif executable.is_a?(Graph)
|
35
|
+
executable.execute(state)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
class Graph
|
6
|
+
class QuantumEdge
|
7
|
+
#: (Roast::Graph::Node, Proc) -> void
|
8
|
+
def initialize(from_node, to_proc)
|
9
|
+
@from_node = from_node
|
10
|
+
@to_proc = to_proc
|
11
|
+
end
|
12
|
+
|
13
|
+
#: (Hash[untyped, untyped], Hash[Symbol, Roast::Graph::Node]) -> Array[Roast::Graph::Edge]
|
14
|
+
def collapse(state, nodes)
|
15
|
+
to_node_names = @to_proc.call(state)
|
16
|
+
to_node_names = to_node_names.is_a?(Array) ? to_node_names : [to_node_names]
|
17
|
+
|
18
|
+
to_node_names.map do |to_node_name|
|
19
|
+
to_node = nodes[to_node_name]
|
20
|
+
raise Error, "No node found with name #{to_node_name.inspect}" if to_node.nil?
|
21
|
+
|
22
|
+
Edge.new(@from_node, to_node)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Roast
|
5
|
+
class Graph
|
6
|
+
class StateConflictError < Error; end
|
7
|
+
|
8
|
+
class ThreadedExec
|
9
|
+
def initialize(nodes, og_state)
|
10
|
+
@nodes = nodes
|
11
|
+
@og_state = og_state
|
12
|
+
end
|
13
|
+
|
14
|
+
#: () -> Hash[untyped, untyped]
|
15
|
+
def async_execute
|
16
|
+
states = threaded_execute(@nodes, @og_state)
|
17
|
+
merge_states!(@og_state, states)
|
18
|
+
@og_state
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns a hash of the new states for each node.
|
22
|
+
#: (Array[Node], Hash[untyped, untyped]) -> Hash[Symbol, Hash[untyped, untyped]]
|
23
|
+
def threaded_execute(nodes, og_state)
|
24
|
+
states = {}
|
25
|
+
threads = nodes.map do |current_node|
|
26
|
+
states[current_node.name] = og_state.dup
|
27
|
+
Thread.new do
|
28
|
+
current_node.execute(states[current_node.name])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
threads.map(&:value)
|
33
|
+
|
34
|
+
states
|
35
|
+
end
|
36
|
+
|
37
|
+
#: (Hash, Hash) -> void
|
38
|
+
def merge_states!(orig_state, new_states)
|
39
|
+
orig_state.merge!(merge_new_states(orig_state, new_states))
|
40
|
+
end
|
41
|
+
|
42
|
+
# We merge all the new states together first so we can catch any keys that were modified by multiple threads.
|
43
|
+
#: (Hash[untyped, untyped], Hash[Symbol, Hash[untyped, untyped]]) -> Hash[untyped, untyped]
|
44
|
+
def merge_new_states(orig_state, new_states)
|
45
|
+
# We only care about what the threads have changes from the original state.
|
46
|
+
changed_states = changed_states(orig_state, new_states)
|
47
|
+
|
48
|
+
return {} if changed_states.empty?
|
49
|
+
|
50
|
+
# Grab some entry to be the one we merge all the other changes into.
|
51
|
+
base_node_name, base_changed_state = T.must(changed_states.shift)
|
52
|
+
base_node_name = T.cast(base_node_name, Symbol)
|
53
|
+
base_changed_state = T.cast(base_changed_state, T::Hash[T.untyped, T.untyped])
|
54
|
+
|
55
|
+
changed_states.each do |node_name, changed_state|
|
56
|
+
base_changed_state.merge!(changed_state) do |key, old_value, new_value|
|
57
|
+
raise_state_conflict(key, old_value, new_value, [base_node_name, node_name])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
base_changed_state
|
62
|
+
end
|
63
|
+
|
64
|
+
#: (Hash, Hash[Symbol, Hash]) -> Hash[Symbol, Hash]
|
65
|
+
def changed_states(orig_state, new_states)
|
66
|
+
new_states.transform_values do |new_state|
|
67
|
+
changed_state_entries(orig_state, new_state)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Filter new states content to only include keys that were modified by the new states.
|
72
|
+
#: (Hash, Hash) -> Hash
|
73
|
+
def changed_state_entries(orig_state, new_state)
|
74
|
+
new_state.reject do |key, value|
|
75
|
+
orig_state[key] == value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
#: (Symbol, untyped, untyped, Array[Symbol]) -> void
|
80
|
+
def raise_state_conflict(key, old_value, new_value, conflicting_nodes)
|
81
|
+
raise StateConflictError, <<~CONFLICT.chomp
|
82
|
+
Parallel nodes modified the same state key.
|
83
|
+
Conflicting nodes: #{conflicting_nodes.join(", ")}
|
84
|
+
Key: :#{key}
|
85
|
+
Old value:
|
86
|
+
#{old_value.inspect}
|
87
|
+
New value:
|
88
|
+
#{new_value.inspect}
|
89
|
+
CONFLICT
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|