roast-ai 0.4.9 → 0.4.10
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/Gemfile +6 -7
- data/Gemfile.lock +14 -2
- data/dsl/demo/Gemfile +4 -0
- data/dsl/demo/Gemfile.lock +120 -0
- data/dsl/demo/cogs/local.rb +15 -0
- data/dsl/demo/simple_external_cog.rb +17 -0
- data/dsl/plugin-gem-example/.gitignore +8 -0
- data/dsl/plugin-gem-example/Gemfile +13 -0
- data/dsl/plugin-gem-example/Gemfile.lock +178 -0
- data/dsl/plugin-gem-example/lib/other.rb +17 -0
- data/dsl/plugin-gem-example/lib/plugin_gem_example.rb +5 -0
- data/dsl/plugin-gem-example/lib/simple.rb +15 -0
- data/dsl/plugin-gem-example/lib/version.rb +10 -0
- data/dsl/plugin-gem-example/plugin-gem-example.gemspec +28 -0
- data/dsl/prototype.rb +11 -3
- data/dsl/scoped_executors.rb +28 -0
- data/dsl/simple_chat.rb +12 -0
- data/dsl/step_communication.rb +10 -4
- data/lib/roast/dsl/cog/config.rb +6 -1
- data/lib/roast/dsl/cog/input.rb +30 -0
- data/lib/roast/dsl/cog/output.rb +24 -0
- data/lib/roast/dsl/cog/registry.rb +39 -0
- data/lib/roast/dsl/cog/stack.rb +2 -1
- data/lib/roast/dsl/cog/store.rb +8 -5
- data/lib/roast/dsl/cog.rb +49 -28
- data/lib/roast/dsl/cog_input_context.rb +9 -0
- data/lib/roast/dsl/cog_input_manager.rb +47 -0
- data/lib/roast/dsl/cogs/chat.rb +78 -0
- data/lib/roast/dsl/cogs/cmd.rb +97 -20
- data/lib/roast/dsl/cogs/execute.rb +46 -0
- data/lib/roast/dsl/cogs/graph.rb +3 -3
- data/lib/roast/dsl/config_context.rb +2 -47
- data/lib/roast/dsl/config_manager.rb +96 -0
- data/lib/roast/dsl/execution_context.rb +9 -0
- data/lib/roast/dsl/execution_manager.rb +137 -0
- data/lib/roast/dsl/workflow.rb +113 -0
- data/lib/roast/version.rb +1 -1
- data/lib/roast.rb +3 -2
- data/roast.gemspec +1 -0
- data/sorbet/config +1 -0
- data/sorbet/rbi/gems/marcel@1.1.0.rbi +239 -0
- data/sorbet/rbi/gems/{rack@2.2.18.rbi → rack@2.2.19.rbi} +55 -38
- data/sorbet/rbi/gems/ruby_llm@1.8.2.rbi +5703 -0
- data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +17 -0
- data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +6 -0
- data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +17 -0
- metadata +45 -8
- data/lib/roast/dsl/cog_execution_context.rb +0 -29
- data/lib/roast/dsl/cogs.rb +0 -65
- data/lib/roast/dsl/executor.rb +0 -82
- data/lib/roast/dsl/workflow_execution_context.rb +0 -47
- data/sorbet/rbi/gems/rbs-inline@0.12.0.rbi +0 -2170
- data/sorbet/rbi/shims/lib/roast/dsl/workflow_execution_context.rbi +0 -11
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Roast
|
|
5
|
+
module DSL
|
|
6
|
+
class Cog
|
|
7
|
+
# Abstract parent class for inputs provided to a cog when it runs.
|
|
8
|
+
# Cogs should extend this class with their own input types
|
|
9
|
+
# Input classes should be instantiatable with a no-arg constructor,
|
|
10
|
+
# and expose methods to incrementally set their values.
|
|
11
|
+
class Input
|
|
12
|
+
class InputError < Roast::Error; end
|
|
13
|
+
class InvalidInputError < InputError; end
|
|
14
|
+
|
|
15
|
+
# Validate that the input instance has all required parameters set in an acceptable manner.
|
|
16
|
+
# Inheriting cog must implement this for its input class.
|
|
17
|
+
#: () -> void
|
|
18
|
+
def validate!
|
|
19
|
+
raise NotImplementedError
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Use the value returned from the cog's input block to attempt to coerce the input to a valid state.
|
|
23
|
+
# to coerce the input to a valid state.
|
|
24
|
+
# Inheriting cog may implement this for its input class; it is optional
|
|
25
|
+
#: (untyped) -> void
|
|
26
|
+
def coerce(input_return_value); end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Roast
|
|
5
|
+
module DSL
|
|
6
|
+
class Cog
|
|
7
|
+
# Generic output from running a cog.
|
|
8
|
+
# Cogs should extend this class with their own output types.
|
|
9
|
+
class Output
|
|
10
|
+
#: bool
|
|
11
|
+
attr_reader :success
|
|
12
|
+
|
|
13
|
+
#: bool
|
|
14
|
+
attr_reader :abort
|
|
15
|
+
|
|
16
|
+
#: (?success: bool, ?abort_workflow: bool) -> void
|
|
17
|
+
def initialize(success: true, abort_workflow: false)
|
|
18
|
+
@success = success #: bool
|
|
19
|
+
@abort = abort_workflow #: bool
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Roast
|
|
5
|
+
module DSL
|
|
6
|
+
class Cog
|
|
7
|
+
class Registry
|
|
8
|
+
class CogRegistryError < Roast::Error; end
|
|
9
|
+
class CouldNotDeriveCogNameError < CogRegistryError; end
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@cogs = {}
|
|
13
|
+
create_registration(Cogs::Cmd)
|
|
14
|
+
create_registration(Cogs::Chat)
|
|
15
|
+
create_registration(Cogs::Execute)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
#: Hash[Symbol, singleton(Cog)]
|
|
19
|
+
attr_reader :cogs
|
|
20
|
+
|
|
21
|
+
#: (singleton(Roast::DSL::Cog)) -> void
|
|
22
|
+
def use(cog_class)
|
|
23
|
+
reg = create_registration(cog_class)
|
|
24
|
+
cogs[reg.first] = reg.second
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
#: (singleton(Roast::DSL::Cog)) -> Array(Symbol, singleton(Cog))
|
|
30
|
+
def create_registration(cog_class)
|
|
31
|
+
cog_class_name = cog_class.name
|
|
32
|
+
raise CouldNotDeriveCogNameError if cog_class_name.nil?
|
|
33
|
+
|
|
34
|
+
[cog_class_name.demodulize.underscore.to_sym, cog_class]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/roast/dsl/cog/stack.rb
CHANGED
data/lib/roast/dsl/cog/store.rb
CHANGED
|
@@ -9,17 +9,20 @@ module Roast
|
|
|
9
9
|
|
|
10
10
|
delegate :[], to: :store
|
|
11
11
|
|
|
12
|
+
#: Hash[Symbol, Cog]
|
|
13
|
+
attr_reader :store
|
|
14
|
+
|
|
15
|
+
#: () -> void
|
|
16
|
+
def initialize
|
|
17
|
+
@store = {}
|
|
18
|
+
end
|
|
19
|
+
|
|
12
20
|
#: (Symbol, Roast::DSL::Cog) -> Roast::DSL::Cog
|
|
13
21
|
def insert(id, inst)
|
|
14
22
|
raise CogAlreadyDefinedError if store.key?(id)
|
|
15
23
|
|
|
16
24
|
store[id] = inst
|
|
17
25
|
end
|
|
18
|
-
|
|
19
|
-
#: () -> Hash[Symbol, Roast::DSL::Cog]
|
|
20
|
-
def store
|
|
21
|
-
@store ||= {}
|
|
22
|
-
end
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
end
|
data/lib/roast/dsl/cog.rb
CHANGED
|
@@ -4,67 +4,88 @@
|
|
|
4
4
|
module Roast
|
|
5
5
|
module DSL
|
|
6
6
|
class Cog
|
|
7
|
-
class
|
|
7
|
+
class CogError < Roast::Error; end
|
|
8
8
|
|
|
9
|
-
class
|
|
10
|
-
def on_create
|
|
11
|
-
eigen = self
|
|
12
|
-
proc do |instance_name = Random.uuid, &action|
|
|
13
|
-
#: self as Roast::DSL::WorkflowExecutionContext
|
|
14
|
-
add_cog_instance(instance_name, eigen.new(instance_name, action))
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def on_config
|
|
19
|
-
eigen = self
|
|
20
|
-
proc do |cog_name = nil, &configuration|
|
|
21
|
-
#: self as Roast::DSL::ConfigContext
|
|
22
|
-
config_object = if cog_name.nil?
|
|
23
|
-
fetch_execution_scope(eigen)
|
|
24
|
-
else
|
|
25
|
-
fetch_or_create_cog_config(eigen, cog_name)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
config_object.instance_exec(&configuration) if configuration
|
|
29
|
-
end
|
|
30
|
-
end
|
|
9
|
+
class CogAlreadyRanError < CogError; end
|
|
31
10
|
|
|
11
|
+
class << self
|
|
12
|
+
#: () -> singleton(Cog::Config)
|
|
32
13
|
def config_class
|
|
33
14
|
@config_class ||= find_child_config_or_default
|
|
34
15
|
end
|
|
35
16
|
|
|
17
|
+
#: () -> singleton(Cog::Input)
|
|
18
|
+
def input_class
|
|
19
|
+
@input_class ||= find_child_input_or_default #: singleton(Cog::Input)?
|
|
20
|
+
end
|
|
21
|
+
|
|
36
22
|
private
|
|
37
23
|
|
|
24
|
+
#: () -> singleton(Cog::Config)
|
|
38
25
|
def find_child_config_or_default
|
|
39
26
|
config_constant = "#{name}::Config"
|
|
40
27
|
const_defined?(config_constant) ? const_get(config_constant) : Cog::Config # rubocop:disable Sorbet/ConstantsFromStrings
|
|
41
28
|
end
|
|
29
|
+
|
|
30
|
+
#: () -> singleton(Cog::Input)
|
|
31
|
+
def find_child_input_or_default
|
|
32
|
+
input_constant = "#{name}::Input"
|
|
33
|
+
const_defined?(input_constant) ? const_get(input_constant) : Cog::Input # rubocop:disable Sorbet/ConstantsFromStrings
|
|
34
|
+
end
|
|
42
35
|
end
|
|
43
36
|
|
|
44
|
-
|
|
37
|
+
#: Symbol
|
|
38
|
+
attr_reader :name
|
|
45
39
|
|
|
40
|
+
#: Cog::Output?
|
|
41
|
+
attr_reader :output
|
|
42
|
+
|
|
43
|
+
#: (Symbol, ^(Cog::Input) -> untyped) -> void
|
|
46
44
|
def initialize(name, cog_input_proc)
|
|
47
45
|
@name = name
|
|
48
|
-
@cog_input_proc = cog_input_proc
|
|
49
|
-
@
|
|
46
|
+
@cog_input_proc = cog_input_proc #: ^(Cog::Input) -> untyped
|
|
47
|
+
@output = nil #: Cog::Output?
|
|
48
|
+
@finished = false #: bool
|
|
49
|
+
|
|
50
|
+
# Make sure a config is always defined, so we don't have to worry about nils
|
|
51
|
+
@config = self.class.config_class.new #: Cog::Config
|
|
50
52
|
end
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
#: (Cog::Config, CogInputContext) -> void
|
|
55
|
+
def run!(config, input_context)
|
|
53
56
|
raise CogAlreadyRanError if ran?
|
|
54
57
|
|
|
55
58
|
@config = config
|
|
56
|
-
|
|
59
|
+
input_instance = self.class.input_class.new
|
|
60
|
+
input_return = input_context.instance_exec(input_instance, &@cog_input_proc) if @cog_input_proc
|
|
61
|
+
coerce_and_validate_input!(input_instance, input_return)
|
|
62
|
+
@output = execute(input_instance)
|
|
57
63
|
@finished = true
|
|
58
64
|
end
|
|
59
65
|
|
|
66
|
+
#: () -> bool
|
|
60
67
|
def ran?
|
|
61
68
|
@finished
|
|
62
69
|
end
|
|
63
70
|
|
|
64
71
|
# Inheriting cog must implement this
|
|
72
|
+
#: (Cog::Input) -> Cog::Output
|
|
65
73
|
def execute(input)
|
|
66
74
|
raise NotImplementedError
|
|
67
75
|
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
#: (Cog::Input, untyped) -> void
|
|
80
|
+
def coerce_and_validate_input!(input, return_value)
|
|
81
|
+
# Check if the input is already valid
|
|
82
|
+
input.validate!
|
|
83
|
+
rescue Cog::Input::InvalidInputError
|
|
84
|
+
# If it's not valid, attempt to coerce if possible
|
|
85
|
+
input.coerce(return_value)
|
|
86
|
+
# Re-validate because coerce! should not be responsible for validation
|
|
87
|
+
input.validate!
|
|
88
|
+
end
|
|
68
89
|
end
|
|
69
90
|
end
|
|
70
91
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Roast
|
|
5
|
+
module DSL
|
|
6
|
+
# Context in which an individual cog block within the `execute` block of a workflow is evaluated
|
|
7
|
+
class CogInputManager
|
|
8
|
+
class CogOutputAccessError < Roast::Error; end
|
|
9
|
+
class CogNotYetRunError < CogOutputAccessError; end
|
|
10
|
+
class CogNotDefinedError < CogOutputAccessError; end
|
|
11
|
+
|
|
12
|
+
#: (Cog::Registry, Cog::Store) -> void
|
|
13
|
+
def initialize(cog_registry, cogs)
|
|
14
|
+
@cog_registry = cog_registry
|
|
15
|
+
@cogs = cogs
|
|
16
|
+
@context = CogInputContext.new
|
|
17
|
+
bind_registered_cogs
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
#: CogInputContext
|
|
21
|
+
attr_reader :context
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
#: () -> void
|
|
26
|
+
def bind_registered_cogs
|
|
27
|
+
@cog_registry.cogs.keys.each(&method(:bind_cog))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#: (Symbol) -> void
|
|
31
|
+
def bind_cog(cog_method_name)
|
|
32
|
+
cog_output_method = method(:cog_output)
|
|
33
|
+
@context.instance_eval do
|
|
34
|
+
define_singleton_method(cog_method_name, proc { |cog_name| cog_output_method.call(cog_name) })
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#: (Symbol) -> Cog::Output
|
|
39
|
+
def cog_output(cog_name)
|
|
40
|
+
@cogs[cog_name].tap do |cog|
|
|
41
|
+
raise CogNotYetRunError unless cog.ran?
|
|
42
|
+
raise CogNotDefinedError unless cog.output.present?
|
|
43
|
+
end.output
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Roast
|
|
5
|
+
module DSL
|
|
6
|
+
module Cogs
|
|
7
|
+
class Chat < Cog
|
|
8
|
+
class Input < Cog::Input
|
|
9
|
+
#: String?
|
|
10
|
+
attr_accessor :prompt
|
|
11
|
+
|
|
12
|
+
#: () -> void
|
|
13
|
+
def initialize
|
|
14
|
+
super
|
|
15
|
+
@prompt = nil #: String?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
#: () -> void
|
|
19
|
+
def validate!
|
|
20
|
+
raise Cog::Input::InvalidInputError, "'prompt' is required" unless prompt.present?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#: (untyped) -> void
|
|
24
|
+
def coerce!(input_return_value)
|
|
25
|
+
if input_return_value.is_a?(String)
|
|
26
|
+
self.prompt = input_return_value
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Output < Cog::Output
|
|
32
|
+
#: String
|
|
33
|
+
attr_reader :response
|
|
34
|
+
|
|
35
|
+
#: (String response) -> void
|
|
36
|
+
def initialize(response)
|
|
37
|
+
super()
|
|
38
|
+
@response = response
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Config < Cog::Config
|
|
43
|
+
#: () -> String?
|
|
44
|
+
def openai_api_key
|
|
45
|
+
@values[:openai_api_key] ||= ENV["OPENAI_API_KEY"]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#: () -> String?
|
|
49
|
+
def openai_api_base_url
|
|
50
|
+
@values[:openai_api_base_url] ||= ENV["OPENAI_API_BASE_URL"]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#: (Input) -> Output
|
|
55
|
+
def execute(input)
|
|
56
|
+
config = @config #: as Config
|
|
57
|
+
RubyLLM.configure do |ruby_llm_config|
|
|
58
|
+
ruby_llm_config.openai_api_key = config.openai_api_key
|
|
59
|
+
ruby_llm_config.openai_api_base = config.openai_api_base_url
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
chat = RubyLLM.chat
|
|
63
|
+
resp = chat.ask(input.prompt)
|
|
64
|
+
puts "Model: #{resp.model_id}"
|
|
65
|
+
puts "Role: #{resp.role}"
|
|
66
|
+
puts "Input Tokens: #{resp.input_tokens}"
|
|
67
|
+
puts "Output Tokens: #{resp.output_tokens}"
|
|
68
|
+
|
|
69
|
+
chat.messages.each do |message|
|
|
70
|
+
puts "[#{message.role.to_s.upcase}] #{message.content}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
Output.new(resp.content)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/roast/dsl/cogs/cmd.rb
CHANGED
|
@@ -5,49 +5,126 @@ module Roast
|
|
|
5
5
|
module DSL
|
|
6
6
|
module Cogs
|
|
7
7
|
class Cmd < Cog
|
|
8
|
-
class
|
|
8
|
+
class Input < Cog::Input
|
|
9
9
|
#: String?
|
|
10
|
-
|
|
10
|
+
attr_accessor :command
|
|
11
11
|
|
|
12
|
-
#: String
|
|
12
|
+
#: Array[String]
|
|
13
|
+
attr_accessor :args
|
|
14
|
+
|
|
15
|
+
#: () -> void
|
|
16
|
+
def initialize
|
|
17
|
+
super
|
|
18
|
+
@command = nil
|
|
19
|
+
@args = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#: () -> void
|
|
23
|
+
def validate!
|
|
24
|
+
raise Cog::Input::InvalidInputError, "'command' is required" unless command.present?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#: (String | Array[untyped]) -> void
|
|
28
|
+
def coerce(input_return_value)
|
|
29
|
+
case input_return_value
|
|
30
|
+
when String
|
|
31
|
+
self.command = input_return_value
|
|
32
|
+
when Array
|
|
33
|
+
input_return_value.map!(&:to_s)
|
|
34
|
+
self.command = input_return_value.shift
|
|
35
|
+
self.args = input_return_value
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Output < Cog::Output
|
|
41
|
+
#: String
|
|
42
|
+
attr_reader :out
|
|
43
|
+
|
|
44
|
+
#: String
|
|
13
45
|
attr_reader :err
|
|
14
46
|
|
|
15
|
-
#: Process::Status
|
|
47
|
+
#: Process::Status
|
|
16
48
|
attr_reader :status
|
|
17
49
|
|
|
18
|
-
#: (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@command_output = output
|
|
25
|
-
@err = error
|
|
26
|
-
@status = status
|
|
50
|
+
#: ( String, String, Process::Status) -> void
|
|
51
|
+
def initialize(out, err, status)
|
|
52
|
+
super()
|
|
53
|
+
@out = out #: String
|
|
54
|
+
@err = err #: String
|
|
55
|
+
@status = status #: Process::Status
|
|
27
56
|
end
|
|
28
57
|
end
|
|
29
58
|
|
|
30
59
|
class Config < Cog::Config
|
|
31
60
|
#: () -> void
|
|
32
61
|
def print_all!
|
|
33
|
-
@values[:
|
|
62
|
+
@values[:print_stdout] = true
|
|
63
|
+
@values[:print_stderr] = true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
#: () -> void
|
|
67
|
+
def print_stdout!
|
|
68
|
+
@values[:print_stdout] = true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
#: () -> void
|
|
72
|
+
def print_stderr!
|
|
73
|
+
@values[:print_stderr] = true
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#: () -> bool
|
|
77
|
+
def print_stdout?
|
|
78
|
+
!!@values[:print_stdout]
|
|
34
79
|
end
|
|
35
80
|
|
|
36
81
|
#: () -> bool
|
|
37
|
-
def
|
|
38
|
-
!!@values[:
|
|
82
|
+
def print_stderr?
|
|
83
|
+
!!@values[:print_stderr]
|
|
39
84
|
end
|
|
40
85
|
|
|
86
|
+
#: () -> void
|
|
41
87
|
def display!
|
|
42
88
|
print_all!
|
|
43
89
|
end
|
|
44
90
|
end
|
|
45
91
|
|
|
46
|
-
#: (
|
|
92
|
+
#: (Input) -> Output
|
|
47
93
|
def execute(input)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
94
|
+
config = @config #: as Config
|
|
95
|
+
result = T.unsafe(Roast::Helpers::CmdRunner).popen3(input.command, *input.args) do |stdin, stdout, stderr, wait_thread|
|
|
96
|
+
stdin.close
|
|
97
|
+
command_output = ""
|
|
98
|
+
command_error = ""
|
|
99
|
+
|
|
100
|
+
# Thread to read and accumulate stdout
|
|
101
|
+
stdout_thread = Thread.new do
|
|
102
|
+
stdout.each_line do |line|
|
|
103
|
+
command_output += line
|
|
104
|
+
$stdout.puts(line) if config.print_stdout?
|
|
105
|
+
end
|
|
106
|
+
rescue IOError => e
|
|
107
|
+
Roast::Helpers::Logger.debug("IOError while reading stdout: #{e.message}")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Thread to read and accumulate stderr
|
|
111
|
+
stderr_thread = Thread.new do
|
|
112
|
+
stderr.each_line do |line|
|
|
113
|
+
command_error += line
|
|
114
|
+
$stderr.puts(line) if config.print_stderr?
|
|
115
|
+
end
|
|
116
|
+
rescue IOError => e
|
|
117
|
+
Roast::Helpers::Logger.debug("IOError while reading stderr: #{e.message}")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Wait for threads to finish
|
|
121
|
+
stdout_thread.join
|
|
122
|
+
stderr_thread.join
|
|
123
|
+
|
|
124
|
+
[command_output, command_error, wait_thread.value]
|
|
125
|
+
end #: as [String, String, Process::Status]
|
|
126
|
+
|
|
127
|
+
Output.new(*result)
|
|
51
128
|
end
|
|
52
129
|
end
|
|
53
130
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Roast
|
|
5
|
+
module DSL
|
|
6
|
+
module Cogs
|
|
7
|
+
class Execute < Cog
|
|
8
|
+
class Input < Cog::Input
|
|
9
|
+
#: Symbol?
|
|
10
|
+
attr_accessor :scope
|
|
11
|
+
|
|
12
|
+
#: () -> void
|
|
13
|
+
def initialize
|
|
14
|
+
super
|
|
15
|
+
@scope = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
#: () -> void
|
|
19
|
+
def validate!
|
|
20
|
+
raise Cog::Input::InvalidInputError, "'scope' is required" unless scope.present?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#: (Symbol) -> void
|
|
24
|
+
def coerce(input_return_value)
|
|
25
|
+
self.scope = input_return_value
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#: (Symbol, ^(Input) -> untyped, ^(Input) -> void) -> void
|
|
30
|
+
def initialize(name, cog_input_proc, trigger)
|
|
31
|
+
# NOTE: Sorbet expects the proc passed to super to be declared as taking a Cog::Input explicitly,
|
|
32
|
+
# not a subclass of Cog::Input.
|
|
33
|
+
cog_input_proc = cog_input_proc #: as ^(Cog::Input) -> untyped
|
|
34
|
+
super(name, cog_input_proc)
|
|
35
|
+
@trigger = trigger
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#: (Input) -> Cog::Output
|
|
39
|
+
def execute(input)
|
|
40
|
+
@trigger.call(input)
|
|
41
|
+
Cog::Output.new
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/roast/dsl/cogs/graph.rb
CHANGED
|
@@ -8,12 +8,12 @@ module Roast
|
|
|
8
8
|
#: Proc
|
|
9
9
|
attr_reader :block
|
|
10
10
|
|
|
11
|
-
#: (Symbol) { (
|
|
11
|
+
#: (Symbol) { (Cog::Input) -> untyped } -> void
|
|
12
12
|
def initialize(name, &block)
|
|
13
13
|
@name = name
|
|
14
14
|
@block = block
|
|
15
15
|
@graph = Roast::Graph.new
|
|
16
|
-
super(name,
|
|
16
|
+
super(name, block)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
#: () -> void
|
|
@@ -36,7 +36,7 @@ module Roast
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Populates the provided graph in-place, with the definition of how to populate in the block
|
|
39
|
-
#: (
|
|
39
|
+
#: (Cog::Input) -> void
|
|
40
40
|
def populate!(graph)
|
|
41
41
|
return if @block.nil?
|
|
42
42
|
|
|
@@ -3,52 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
module Roast
|
|
5
5
|
module DSL
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
6
|
+
# Context in which the `config` blocks of a workflow definition are evaluated
|
|
7
|
+
class ConfigContext; end
|
|
53
8
|
end
|
|
54
9
|
end
|