roast-ai 0.4.9 → 0.5.0
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/.claude/commands/docs/write-comments.md +36 -0
- data/.github/CODEOWNERS +1 -1
- data/.github/workflows/ci.yaml +10 -6
- data/.gitignore +0 -1
- data/.rubocop.yml +7 -1
- data/CLAUDE.md +2 -2
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +18 -18
- data/Gemfile.lock +46 -57
- data/README.md +118 -1432
- data/README_LEGACY.md +1464 -0
- data/Rakefile +39 -4
- data/dev.yml +29 -0
- data/dsl/agent_sessions.rb +20 -0
- data/dsl/async_cogs.rb +49 -0
- data/dsl/async_cogs_complex.rb +67 -0
- data/dsl/call.rb +44 -0
- data/dsl/collect_from.rb +72 -0
- 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/json_output.rb +28 -0
- data/dsl/map.rb +55 -0
- data/dsl/map_reduce.rb +37 -0
- data/dsl/map_with_index.rb +49 -0
- data/dsl/next_break.rb +40 -0
- data/dsl/next_break_parallel.rb +44 -0
- data/dsl/outputs.rb +39 -0
- data/dsl/outputs_bang.rb +36 -0
- data/dsl/parallel_map.rb +37 -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/prompts/simple_prompt.md.erb +3 -0
- data/dsl/prototype.rb +10 -4
- data/dsl/repeat_loop_results.rb +53 -0
- data/dsl/ruby_cog.rb +72 -0
- data/dsl/simple_agent.rb +18 -0
- data/dsl/simple_chat.rb +26 -0
- data/dsl/simple_repeat.rb +29 -0
- data/dsl/skip.rb +36 -0
- data/dsl/step_communication.rb +10 -5
- data/dsl/targets_and_params.rb +57 -0
- data/dsl/temperature.rb +17 -0
- data/dsl/temporary_directory.rb +22 -0
- data/dsl/tutorial/01_your_first_workflow/README.md +179 -0
- data/dsl/tutorial/01_your_first_workflow/configured_chat.rb +33 -0
- data/dsl/tutorial/01_your_first_workflow/hello.rb +23 -0
- data/dsl/tutorial/02_chaining_cogs/README.md +310 -0
- data/dsl/tutorial/02_chaining_cogs/code_review.rb +104 -0
- data/dsl/tutorial/02_chaining_cogs/session_resumption.rb +92 -0
- data/dsl/tutorial/02_chaining_cogs/simple_chain.rb +84 -0
- data/dsl/tutorial/03_targets_and_params/README.md +230 -0
- data/dsl/tutorial/03_targets_and_params/multiple_targets.rb +65 -0
- data/dsl/tutorial/03_targets_and_params/single_target.rb +65 -0
- data/dsl/tutorial/04_configuration_options/README.md +209 -0
- data/dsl/tutorial/04_configuration_options/control_display_and_temperature.rb +104 -0
- data/dsl/tutorial/04_configuration_options/simple_config.rb +68 -0
- data/dsl/tutorial/05_control_flow/README.md +156 -0
- data/dsl/tutorial/05_control_flow/conditional_execution.rb +62 -0
- data/dsl/tutorial/05_control_flow/handling_failures.rb +77 -0
- data/dsl/tutorial/06_reusable_scopes/README.md +172 -0
- data/dsl/tutorial/06_reusable_scopes/accessing_scope_outputs.rb +126 -0
- data/dsl/tutorial/06_reusable_scopes/basic_scope.rb +63 -0
- data/dsl/tutorial/06_reusable_scopes/parameterized_scope.rb +78 -0
- data/dsl/tutorial/07_processing_collections/README.md +152 -0
- data/dsl/tutorial/07_processing_collections/basic_map.rb +70 -0
- data/dsl/tutorial/07_processing_collections/parallel_map.rb +74 -0
- data/dsl/tutorial/08_iterative_workflows/README.md +231 -0
- data/dsl/tutorial/08_iterative_workflows/basic_repeat.rb +57 -0
- data/dsl/tutorial/08_iterative_workflows/conditional_break.rb +57 -0
- data/dsl/tutorial/09_async_cogs/README.md +197 -0
- data/dsl/tutorial/09_async_cogs/basic_async.rb +38 -0
- data/dsl/tutorial/README.md +222 -0
- data/dsl/working_directory.rb +16 -0
- data/exe/roast +1 -1
- data/internal/documentation/architectural-notes.md +115 -0
- data/internal/documentation/doc-comments-external.md +686 -0
- data/internal/documentation/doc-comments-internal.md +342 -0
- data/internal/documentation/doc-comments.md +211 -0
- data/lib/roast/dsl/cog/config.rb +280 -4
- data/lib/roast/dsl/cog/input.rb +73 -0
- data/lib/roast/dsl/cog/output.rb +313 -0
- data/lib/roast/dsl/cog/registry.rb +71 -0
- data/lib/roast/dsl/cog/stack.rb +3 -2
- data/lib/roast/dsl/cog/store.rb +11 -8
- data/lib/roast/dsl/cog.rb +108 -31
- data/lib/roast/dsl/cog_input_context.rb +44 -0
- data/lib/roast/dsl/cog_input_manager.rb +156 -0
- data/lib/roast/dsl/cogs/agent/config.rb +465 -0
- data/lib/roast/dsl/cogs/agent/input.rb +81 -0
- data/lib/roast/dsl/cogs/agent/output.rb +59 -0
- data/lib/roast/dsl/cogs/agent/provider.rb +51 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/claude_invocation.rb +185 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/message.rb +73 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/assistant_message.rb +36 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/result_message.rb +61 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/system_message.rb +47 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/text_message.rb +36 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_result_message.rb +47 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/tool_use_message.rb +46 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/unknown_message.rb +27 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/messages/user_message.rb +37 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/tool_result.rb +51 -0
- data/lib/roast/dsl/cogs/agent/providers/claude/tool_use.rb +48 -0
- data/lib/roast/dsl/cogs/agent/providers/claude.rb +31 -0
- data/lib/roast/dsl/cogs/agent/stats.rb +92 -0
- data/lib/roast/dsl/cogs/agent/usage.rb +62 -0
- data/lib/roast/dsl/cogs/agent.rb +75 -0
- data/lib/roast/dsl/cogs/chat/config.rb +453 -0
- data/lib/roast/dsl/cogs/chat/input.rb +92 -0
- data/lib/roast/dsl/cogs/chat/output.rb +64 -0
- data/lib/roast/dsl/cogs/chat/session.rb +68 -0
- data/lib/roast/dsl/cogs/chat.rb +81 -0
- data/lib/roast/dsl/cogs/cmd.rb +291 -27
- data/lib/roast/dsl/cogs/ruby.rb +171 -0
- data/lib/roast/dsl/command_runner.rb +191 -0
- data/lib/roast/dsl/config_context.rb +2 -47
- data/lib/roast/dsl/config_manager.rb +143 -0
- data/lib/roast/dsl/control_flow.rb +41 -0
- data/lib/roast/dsl/execution_context.rb +9 -0
- data/lib/roast/dsl/execution_manager.rb +267 -0
- data/lib/roast/dsl/nil_assertions.rb +23 -0
- data/lib/roast/dsl/system_cog/params.rb +32 -0
- data/lib/roast/dsl/system_cog.rb +36 -0
- data/lib/roast/dsl/system_cogs/call.rb +162 -0
- data/lib/roast/dsl/system_cogs/map.rb +448 -0
- data/lib/roast/dsl/system_cogs/repeat.rb +242 -0
- data/lib/roast/dsl/workflow.rb +123 -0
- data/lib/roast/dsl/workflow_context.rb +20 -0
- data/lib/roast/dsl/workflow_params.rb +24 -0
- data/lib/roast/sorbet_runtime_stub.rb +154 -0
- data/lib/roast/tools/apply_diff.rb +1 -3
- data/lib/roast/tools/cmd.rb +4 -3
- data/lib/roast/tools/read_file.rb +1 -1
- data/lib/roast/tools/update_files.rb +1 -1
- data/lib/roast/tools/write_file.rb +1 -1
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/base_workflow.rb +4 -0
- data/lib/roast/workflow/step_loader.rb +14 -2
- data/lib/roast-ai.rb +4 -0
- data/lib/roast.rb +60 -22
- data/{roast.gemspec → roast-ai.gemspec} +10 -13
- data/sorbet/config +1 -0
- data/sorbet/rbi/gems/async@2.34.0.rbi +1577 -0
- data/sorbet/rbi/gems/cli-kit@5.2.0.rbi +2063 -0
- data/sorbet/rbi/gems/{cli-ui@2.3.0.rbi → cli-ui@2.7.0-6bdefd1d06305e5d6ae312ac76f9c88f88658dda.rbi} +1418 -1013
- data/sorbet/rbi/gems/console@1.34.2.rbi +1193 -0
- data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +50 -0
- data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +35 -0
- data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +41 -0
- data/sorbet/rbi/gems/io-event@1.14.0.rbi +724 -0
- data/sorbet/rbi/gems/marcel@1.1.0.rbi +239 -0
- data/sorbet/rbi/gems/metrics@0.15.0.rbi +9 -0
- data/sorbet/rbi/gems/ruby_llm@1.8.2.rbi +5703 -0
- data/sorbet/rbi/gems/traces@0.18.2.rbi +9 -0
- data/sorbet/rbi/shims/lib/roast/dsl/cog_input_context.rbi +1197 -0
- data/sorbet/rbi/shims/lib/roast/dsl/config_context.rbi +314 -2
- data/sorbet/rbi/shims/lib/roast/dsl/execution_context.rbi +498 -0
- data/sorbet/tapioca/config.yml +6 -0
- data/sorbet/tapioca/require.rb +2 -0
- metadata +198 -34
- data/dsl/less_simple.rb +0 -112
- data/dsl/simple.rb +0 -8
- data/lib/roast/dsl/cog_execution_context.rb +0 -29
- data/lib/roast/dsl/cogs/graph.rb +0 -53
- 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/cgi@0.5.0.rbi +0 -2961
- data/sorbet/rbi/gems/claude_swarm@0.1.19.rbi +0 -568
- data/sorbet/rbi/gems/cli-kit@5.0.1.rbi +0 -1991
- data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +0 -672
- data/sorbet/rbi/gems/dry-core@1.1.0.rbi +0 -1894
- data/sorbet/rbi/gems/dry-inflector@1.2.0.rbi +0 -659
- data/sorbet/rbi/gems/dry-initializer@3.2.0.rbi +0 -781
- data/sorbet/rbi/gems/dry-logic@1.6.0.rbi +0 -1127
- data/sorbet/rbi/gems/dry-schema@1.14.1.rbi +0 -3727
- data/sorbet/rbi/gems/dry-types@1.8.3.rbi +0 -3969
- data/sorbet/rbi/gems/fast-mcp-annotations@1.5.3.rbi +0 -1588
- data/sorbet/rbi/gems/mime-types-data@3.2025.0617.rbi +0 -136
- data/sorbet/rbi/gems/mime-types@3.7.0.rbi +0 -1342
- data/sorbet/rbi/gems/rack@2.2.18.rbi +0 -5659
- data/sorbet/rbi/gems/rbs-inline@0.12.0.rbi +0 -2170
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
- data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18492
- data/sorbet/rbi/shims/lib/roast/dsl/workflow_execution_context.rbi +0 -11
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Roast
|
|
5
|
+
module DSL
|
|
6
|
+
class Cog
|
|
7
|
+
# Registry for managing available cogs in a workflow
|
|
8
|
+
class Registry
|
|
9
|
+
# Parent class for all cog registry errors
|
|
10
|
+
class CogRegistryError < Roast::Error; end
|
|
11
|
+
|
|
12
|
+
# Raised when a cog name cannot be derived from a cog class
|
|
13
|
+
class CouldNotDeriveCogNameError < CogRegistryError; end
|
|
14
|
+
|
|
15
|
+
# Initialize a new cog registry with standard cogs
|
|
16
|
+
#
|
|
17
|
+
# Automatically registers all system cogs and standard cogs provided by core Roast.
|
|
18
|
+
#
|
|
19
|
+
# #### See Also
|
|
20
|
+
# - `use`
|
|
21
|
+
# - `cogs`
|
|
22
|
+
#
|
|
23
|
+
def initialize
|
|
24
|
+
@cogs = {}
|
|
25
|
+
use(SystemCogs::Call)
|
|
26
|
+
use(SystemCogs::Map)
|
|
27
|
+
use(SystemCogs::Repeat)
|
|
28
|
+
use(Cogs::Cmd)
|
|
29
|
+
use(Cogs::Chat)
|
|
30
|
+
use(Cogs::Agent)
|
|
31
|
+
use(Cogs::Ruby)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Hash mapping cog names to cog classes
|
|
35
|
+
#
|
|
36
|
+
# #### See Also
|
|
37
|
+
# - `use`
|
|
38
|
+
#
|
|
39
|
+
#: Hash[Symbol, singleton(Cog)]
|
|
40
|
+
attr_reader :cogs
|
|
41
|
+
|
|
42
|
+
# Register a cog class for use in workflows
|
|
43
|
+
#
|
|
44
|
+
# Adds the provided cog class to the registry, deriving its name from the class name.
|
|
45
|
+
# The cog name is the underscored, demodulized version of the class name
|
|
46
|
+
# (e.g., `Roast::DSL::Cogs::MyCustomCog` becomes `:my_custom_cog`).
|
|
47
|
+
#
|
|
48
|
+
# Raises `CouldNotDeriveCogNameError` if the cog class name cannot be determined.
|
|
49
|
+
#
|
|
50
|
+
# #### See Also
|
|
51
|
+
# - `cogs`
|
|
52
|
+
#
|
|
53
|
+
#: (singleton(Roast::DSL::Cog)) -> void
|
|
54
|
+
def use(cog_class)
|
|
55
|
+
reg = create_registration(cog_class)
|
|
56
|
+
cogs[reg.first] = reg.second
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
#: (singleton(Roast::DSL::Cog)) -> Array(Symbol, singleton(Cog))
|
|
62
|
+
def create_registration(cog_class)
|
|
63
|
+
cog_class_name = cog_class.name
|
|
64
|
+
raise CouldNotDeriveCogNameError if cog_class_name.nil?
|
|
65
|
+
|
|
66
|
+
[cog_class_name.demodulize.underscore.to_sym, cog_class]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/roast/dsl/cog/stack.rb
CHANGED
|
@@ -5,10 +5,11 @@ module Roast
|
|
|
5
5
|
module DSL
|
|
6
6
|
class Cog
|
|
7
7
|
class Stack
|
|
8
|
-
delegate :map, :push, :size,
|
|
8
|
+
delegate :each, :empty?, :last, :map, :push, :size, to: :@queue
|
|
9
9
|
|
|
10
|
+
#: () -> void
|
|
10
11
|
def initialize
|
|
11
|
-
@queue = []
|
|
12
|
+
@queue = [] #: Array[Cog]
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
#: () -> Roast::DSL::Cog?
|
data/lib/roast/dsl/cog/store.rb
CHANGED
|
@@ -7,18 +7,21 @@ module Roast
|
|
|
7
7
|
class Store
|
|
8
8
|
class CogAlreadyDefinedError < Roast::Error; end
|
|
9
9
|
|
|
10
|
-
delegate :[], to: :store
|
|
10
|
+
delegate :[], :key?, to: :store
|
|
11
11
|
|
|
12
|
-
#:
|
|
13
|
-
|
|
14
|
-
raise CogAlreadyDefinedError if store.key?(id)
|
|
12
|
+
#: Hash[Symbol, Cog]
|
|
13
|
+
attr_reader :store
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
#: () -> void
|
|
16
|
+
def initialize
|
|
17
|
+
@store = {}
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
#: () ->
|
|
20
|
-
def
|
|
21
|
-
|
|
20
|
+
#: (Cog) -> Roast::DSL::Cog
|
|
21
|
+
def insert(cog)
|
|
22
|
+
raise CogAlreadyDefinedError, cog.name if store.key?(cog.name)
|
|
23
|
+
|
|
24
|
+
store[cog.name] = cog
|
|
22
25
|
end
|
|
23
26
|
end
|
|
24
27
|
end
|
data/lib/roast/dsl/cog.rb
CHANGED
|
@@ -4,67 +4,144 @@
|
|
|
4
4
|
module Roast
|
|
5
5
|
module DSL
|
|
6
6
|
class Cog
|
|
7
|
-
class
|
|
7
|
+
class CogError < Roast::Error; end
|
|
8
|
+
|
|
9
|
+
class CogAlreadyStartedError < CogError; end
|
|
8
10
|
|
|
9
11
|
class << self
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
#: self as Roast::DSL::WorkflowExecutionContext
|
|
14
|
-
add_cog_instance(instance_name, eigen.new(instance_name, action))
|
|
15
|
-
end
|
|
12
|
+
#: () -> singleton(Cog::Config)
|
|
13
|
+
def config_class
|
|
14
|
+
@config_class ||= find_child_config_or_default
|
|
16
15
|
end
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
17
|
+
#: () -> singleton(Cog::Input)
|
|
18
|
+
def input_class
|
|
19
|
+
@input_class ||= find_child_input_or_default #: singleton(Cog::Input)?
|
|
30
20
|
end
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
#: () -> Symbol
|
|
23
|
+
def generate_fallback_name
|
|
24
|
+
Random.uuid.to_sym
|
|
34
25
|
end
|
|
35
26
|
|
|
36
27
|
private
|
|
37
28
|
|
|
29
|
+
#: () -> singleton(Cog::Config)
|
|
38
30
|
def find_child_config_or_default
|
|
39
31
|
config_constant = "#{name}::Config"
|
|
40
32
|
const_defined?(config_constant) ? const_get(config_constant) : Cog::Config # rubocop:disable Sorbet/ConstantsFromStrings
|
|
41
33
|
end
|
|
34
|
+
|
|
35
|
+
#: () -> singleton(Cog::Input)
|
|
36
|
+
def find_child_input_or_default
|
|
37
|
+
input_constant = "#{name}::Input"
|
|
38
|
+
const_defined?(input_constant) ? const_get(input_constant) : Cog::Input # rubocop:disable Sorbet/ConstantsFromStrings
|
|
39
|
+
end
|
|
42
40
|
end
|
|
43
41
|
|
|
44
|
-
|
|
42
|
+
#: Symbol
|
|
43
|
+
attr_reader :name
|
|
44
|
+
|
|
45
|
+
#: Cog::Output?
|
|
46
|
+
attr_reader :output
|
|
45
47
|
|
|
48
|
+
#: (Symbol, ^(Cog::Input) -> untyped) -> void
|
|
46
49
|
def initialize(name, cog_input_proc)
|
|
47
50
|
@name = name
|
|
48
|
-
@cog_input_proc = cog_input_proc
|
|
49
|
-
@
|
|
51
|
+
@cog_input_proc = cog_input_proc #: ^(Cog::Input) -> untyped
|
|
52
|
+
@output = nil #: Cog::Output?
|
|
53
|
+
@skipped = false #: bool
|
|
54
|
+
@failed = false #: bool
|
|
55
|
+
|
|
56
|
+
# Make sure a config is always defined, so we don't have to worry about nils
|
|
57
|
+
@config = self.class.config_class.new #: untyped
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
#: (Async::Barrier, Cog::Config, CogInputContext, untyped, Integer) -> Async::Task
|
|
61
|
+
def run!(barrier, config, input_context, executor_scope_value, executor_scope_index)
|
|
62
|
+
raise CogAlreadyStartedError if @task
|
|
63
|
+
|
|
64
|
+
@task = barrier.async(finished: false) do |task|
|
|
65
|
+
task.annotate("#{self.class.name.not_nil!.demodulize.camelcase} Cog: #{@name}")
|
|
66
|
+
@config = config
|
|
67
|
+
input_instance = self.class.input_class.new
|
|
68
|
+
input_return = input_context.instance_exec(
|
|
69
|
+
input_instance, executor_scope_value, executor_scope_index, &@cog_input_proc
|
|
70
|
+
) if @cog_input_proc
|
|
71
|
+
coerce_and_validate_input!(input_instance, input_return)
|
|
72
|
+
@output = execute(input_instance)
|
|
73
|
+
rescue ControlFlow::SkipCog
|
|
74
|
+
# TODO: do something with the message passed to skip!
|
|
75
|
+
@skipped = true
|
|
76
|
+
rescue ControlFlow::FailCog => e
|
|
77
|
+
# TODO: do something with the message passed to fail!
|
|
78
|
+
@failed = true
|
|
79
|
+
# TODO: better / cleaner handling in the workflow execution manager for a workflow failure
|
|
80
|
+
# just re-raising this exception for now
|
|
81
|
+
raise e if config.abort_on_failure?
|
|
82
|
+
rescue ControlFlow::Next, ControlFlow::Break => e
|
|
83
|
+
@skipped = true
|
|
84
|
+
raise e
|
|
85
|
+
rescue StandardError => e
|
|
86
|
+
@failed = true
|
|
87
|
+
raise e
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
#: () -> void
|
|
92
|
+
def wait
|
|
93
|
+
@task&.wait
|
|
94
|
+
rescue
|
|
95
|
+
# Do nothing if the cog's task raised an exception. That is handled elsewhere.
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
#: () -> bool
|
|
99
|
+
def started?
|
|
100
|
+
@task.present?
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#: () -> bool
|
|
104
|
+
def skipped?
|
|
105
|
+
@skipped
|
|
50
106
|
end
|
|
51
107
|
|
|
52
|
-
|
|
53
|
-
|
|
108
|
+
#: () -> bool
|
|
109
|
+
def failed?
|
|
110
|
+
@failed || !!@task&.failed?
|
|
111
|
+
end
|
|
54
112
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
113
|
+
#: () -> bool
|
|
114
|
+
def stopped?
|
|
115
|
+
!!@task&.stopped?
|
|
58
116
|
end
|
|
59
117
|
|
|
60
|
-
|
|
61
|
-
|
|
118
|
+
#: () -> bool
|
|
119
|
+
def succeeded?
|
|
120
|
+
# NOTE: explicitly checking `@output == nil` because the `ruby` cog's Output class delegates
|
|
121
|
+
# all missing methods to its `value`, which may be nil when the Output object is actually present.
|
|
122
|
+
@output != nil && @task&.finished? # rubocop:disable Style/NonNilCheck
|
|
62
123
|
end
|
|
63
124
|
|
|
125
|
+
protected
|
|
126
|
+
|
|
64
127
|
# Inheriting cog must implement this
|
|
128
|
+
#: (Cog::Input) -> Cog::Output
|
|
65
129
|
def execute(input)
|
|
66
130
|
raise NotImplementedError
|
|
67
131
|
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
#: (Cog::Input, untyped) -> void
|
|
136
|
+
def coerce_and_validate_input!(input, return_value)
|
|
137
|
+
# Check if the input is already valid
|
|
138
|
+
input.validate!
|
|
139
|
+
rescue Cog::Input::InvalidInputError
|
|
140
|
+
# If it's not valid, attempt to coerce if possible
|
|
141
|
+
input.coerce(return_value)
|
|
142
|
+
# Re-validate because coerce! should not be responsible for validation
|
|
143
|
+
input.validate!
|
|
144
|
+
end
|
|
68
145
|
end
|
|
69
146
|
end
|
|
70
147
|
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Roast
|
|
5
|
+
module DSL
|
|
6
|
+
# Context in which the individual cog input blocks within the `execute` block of a workflow definition are evaluated
|
|
7
|
+
class CogInputContext
|
|
8
|
+
include SystemCogs::Call::InputContext
|
|
9
|
+
include SystemCogs::Map::InputContext
|
|
10
|
+
|
|
11
|
+
class CogInputContextError < Roast::Error; end
|
|
12
|
+
|
|
13
|
+
class ContextNotFoundError < CogInputContextError; end
|
|
14
|
+
|
|
15
|
+
#: (?String?) -> void
|
|
16
|
+
def skip!(message = nil)
|
|
17
|
+
raise ControlFlow::SkipCog, message
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
#: (?String?) -> void
|
|
21
|
+
def fail!(message = nil)
|
|
22
|
+
raise ControlFlow::FailCog, message
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#: (?String?) -> void
|
|
26
|
+
def next!(message = nil)
|
|
27
|
+
raise ControlFlow::Next, message
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#: (?String?) -> void
|
|
31
|
+
def break!(message = nil)
|
|
32
|
+
raise ControlFlow::Break, message
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#: (String, ?Hash) -> String
|
|
36
|
+
def template(path, args = {})
|
|
37
|
+
path = "prompts/#{path}.md.erb" unless File.exist?(path)
|
|
38
|
+
fail!("The prompt #{path} could not be found") unless File.exist?(path)
|
|
39
|
+
|
|
40
|
+
ERB.new(File.read(path)).result_with_hash(args)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
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
|
+
|
|
10
|
+
class CogDoesNotExistError < CogOutputAccessError; end
|
|
11
|
+
|
|
12
|
+
class CogNotYetRunError < CogOutputAccessError; end
|
|
13
|
+
|
|
14
|
+
class CogSkippedError < CogOutputAccessError; end
|
|
15
|
+
|
|
16
|
+
class CogFailedError < CogOutputAccessError; end
|
|
17
|
+
|
|
18
|
+
class CogStoppedError < CogOutputAccessError; end
|
|
19
|
+
|
|
20
|
+
#: (Cog::Registry, Cog::Store, WorkflowContext) -> void
|
|
21
|
+
def initialize(cog_registry, cogs, workflow_context)
|
|
22
|
+
@cog_registry = cog_registry
|
|
23
|
+
@cogs = cogs
|
|
24
|
+
@workflow_context = workflow_context
|
|
25
|
+
@context = CogInputContext.new
|
|
26
|
+
bind_registered_cogs
|
|
27
|
+
bind_workflow_context
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#: CogInputContext
|
|
31
|
+
attr_reader :context
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
#: () -> void
|
|
36
|
+
def bind_registered_cogs
|
|
37
|
+
@cog_registry.cogs.keys.each(&method(:bind_cog))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
#: (Symbol) -> void
|
|
41
|
+
def bind_cog(cog_method_name)
|
|
42
|
+
cog_question_method_name = (cog_method_name.to_s + "?").to_sym
|
|
43
|
+
cog_bang_method_name = (cog_method_name.to_s + "!").to_sym
|
|
44
|
+
cog_output_method = method(:cog_output)
|
|
45
|
+
cog_output_question_method = method(:cog_output?)
|
|
46
|
+
cog_output_bang_method = method(:cog_output!)
|
|
47
|
+
@context.instance_eval do
|
|
48
|
+
define_singleton_method(cog_method_name, proc { |cog_name| cog_output_method.call(cog_name) })
|
|
49
|
+
define_singleton_method(cog_question_method_name, proc { |cog_name| cog_output_question_method.call(cog_name) })
|
|
50
|
+
define_singleton_method(cog_bang_method_name, proc { |cog_name| cog_output_bang_method.call(cog_name) })
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#: (Symbol) -> Cog::Output?
|
|
55
|
+
def cog_output(cog_name)
|
|
56
|
+
cog_output!(cog_name)
|
|
57
|
+
rescue CogOutputAccessError => e
|
|
58
|
+
# Even this method should raise an exception if the requested cog does not exist at all
|
|
59
|
+
raise e if e.is_a?(CogDoesNotExistError)
|
|
60
|
+
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
#: (Symbol) -> bool
|
|
65
|
+
def cog_output?(cog_name)
|
|
66
|
+
!cog_output(cog_name).nil?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#: (Symbol) -> Cog::Output
|
|
70
|
+
def cog_output!(cog_name)
|
|
71
|
+
raise CogDoesNotExistError, cog_name unless @cogs.key?(cog_name)
|
|
72
|
+
|
|
73
|
+
@cogs[cog_name].tap do |cog|
|
|
74
|
+
cog.wait # attempting to access the output of a running cog will block until that cog completes
|
|
75
|
+
raise CogSkippedError, cog_name if cog.skipped?
|
|
76
|
+
raise CogFailedError, cog_name if cog.failed?
|
|
77
|
+
raise CogStoppedError, cog_name if cog.stopped?
|
|
78
|
+
raise CogNotYetRunError, cog_name unless cog.succeeded?
|
|
79
|
+
end.output.deep_dup
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#: () -> void
|
|
83
|
+
def bind_workflow_context
|
|
84
|
+
target_bang_method = method(:target!)
|
|
85
|
+
targets_method = method(:targets)
|
|
86
|
+
arg_question_method = method(:arg?)
|
|
87
|
+
args_method = method(:args)
|
|
88
|
+
kwarg_method = method(:kwarg)
|
|
89
|
+
kwarg_bang_method = method(:kwarg!)
|
|
90
|
+
kwarg_question_method = method(:kwarg?)
|
|
91
|
+
kwargs_method = method(:kwargs)
|
|
92
|
+
tmpdir_method = method(:tmpdir)
|
|
93
|
+
@context.instance_eval do
|
|
94
|
+
define_singleton_method(:target!, proc { target_bang_method.call })
|
|
95
|
+
define_singleton_method(:targets, proc { targets_method.call })
|
|
96
|
+
define_singleton_method(:arg?, proc { |value| arg_question_method.call(value) })
|
|
97
|
+
define_singleton_method(:args, proc { args_method.call })
|
|
98
|
+
define_singleton_method(:kwarg, proc { |key| kwarg_method.call(key) })
|
|
99
|
+
define_singleton_method(:kwarg!, proc { |key| kwarg_bang_method.call(key) })
|
|
100
|
+
define_singleton_method(:kwarg?, proc { |key| kwarg_question_method.call(key) })
|
|
101
|
+
define_singleton_method(:kwargs, proc { kwargs_method.call })
|
|
102
|
+
define_singleton_method(:tmpdir, proc { tmpdir_method.call })
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
#: () -> String
|
|
107
|
+
def target!
|
|
108
|
+
raise ArgumentError, "expected exactly one target" unless @workflow_context.params.targets.length == 1
|
|
109
|
+
|
|
110
|
+
@workflow_context.params.targets.first #: as String
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
#: () -> Array[String]
|
|
114
|
+
def targets
|
|
115
|
+
@workflow_context.params.targets.dup
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#: (Symbol) -> bool
|
|
119
|
+
def arg?(value)
|
|
120
|
+
@workflow_context.params.args.include?(value)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#: () -> Array[Symbol]
|
|
124
|
+
def args
|
|
125
|
+
@workflow_context.params.args.dup
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
#: (Symbol) -> String?
|
|
129
|
+
def kwarg(key)
|
|
130
|
+
@workflow_context.params.kwargs[key]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
#: (Symbol) -> String
|
|
134
|
+
def kwarg!(key)
|
|
135
|
+
raise ArgumentError, "expected keyword argument '#{key}' to be present" unless @workflow_context.params.kwargs.include?(key)
|
|
136
|
+
|
|
137
|
+
@workflow_context.params.kwargs[key] #: as String
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
#: (Symbol) -> bool
|
|
141
|
+
def kwarg?(key)
|
|
142
|
+
@workflow_context.params.kwargs.include?(key)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
#: () -> Hash[Symbol, String]
|
|
146
|
+
def kwargs
|
|
147
|
+
@workflow_context.params.kwargs.dup
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
#: () -> Pathname
|
|
151
|
+
def tmpdir
|
|
152
|
+
Pathname.new(@workflow_context.tmpdir).realpath
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|