language-operator 0.0.1 → 0.1.30

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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +125 -0
  3. data/CHANGELOG.md +53 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +284 -0
  6. data/LICENSE +229 -21
  7. data/Makefile +77 -0
  8. data/README.md +3 -11
  9. data/Rakefile +34 -0
  10. data/bin/aictl +7 -0
  11. data/completions/_aictl +232 -0
  12. data/completions/aictl.bash +121 -0
  13. data/completions/aictl.fish +114 -0
  14. data/docs/architecture/agent-runtime.md +585 -0
  15. data/docs/dsl/agent-reference.md +591 -0
  16. data/docs/dsl/best-practices.md +1078 -0
  17. data/docs/dsl/chat-endpoints.md +895 -0
  18. data/docs/dsl/constraints.md +671 -0
  19. data/docs/dsl/mcp-integration.md +1177 -0
  20. data/docs/dsl/webhooks.md +932 -0
  21. data/docs/dsl/workflows.md +744 -0
  22. data/examples/README.md +569 -0
  23. data/examples/agent_example.rb +86 -0
  24. data/examples/chat_endpoint_agent.rb +118 -0
  25. data/examples/github_webhook_agent.rb +171 -0
  26. data/examples/mcp_agent.rb +158 -0
  27. data/examples/oauth_callback_agent.rb +296 -0
  28. data/examples/stripe_webhook_agent.rb +219 -0
  29. data/examples/webhook_agent.rb +80 -0
  30. data/lib/language_operator/agent/base.rb +110 -0
  31. data/lib/language_operator/agent/executor.rb +440 -0
  32. data/lib/language_operator/agent/instrumentation.rb +54 -0
  33. data/lib/language_operator/agent/metrics_tracker.rb +183 -0
  34. data/lib/language_operator/agent/safety/ast_validator.rb +272 -0
  35. data/lib/language_operator/agent/safety/audit_logger.rb +104 -0
  36. data/lib/language_operator/agent/safety/budget_tracker.rb +175 -0
  37. data/lib/language_operator/agent/safety/content_filter.rb +93 -0
  38. data/lib/language_operator/agent/safety/manager.rb +207 -0
  39. data/lib/language_operator/agent/safety/rate_limiter.rb +150 -0
  40. data/lib/language_operator/agent/safety/safe_executor.rb +115 -0
  41. data/lib/language_operator/agent/scheduler.rb +183 -0
  42. data/lib/language_operator/agent/telemetry.rb +116 -0
  43. data/lib/language_operator/agent/web_server.rb +610 -0
  44. data/lib/language_operator/agent/webhook_authenticator.rb +226 -0
  45. data/lib/language_operator/agent.rb +149 -0
  46. data/lib/language_operator/cli/commands/agent.rb +1252 -0
  47. data/lib/language_operator/cli/commands/cluster.rb +335 -0
  48. data/lib/language_operator/cli/commands/install.rb +404 -0
  49. data/lib/language_operator/cli/commands/model.rb +266 -0
  50. data/lib/language_operator/cli/commands/persona.rb +396 -0
  51. data/lib/language_operator/cli/commands/quickstart.rb +22 -0
  52. data/lib/language_operator/cli/commands/status.rb +156 -0
  53. data/lib/language_operator/cli/commands/tool.rb +537 -0
  54. data/lib/language_operator/cli/commands/use.rb +47 -0
  55. data/lib/language_operator/cli/errors/handler.rb +180 -0
  56. data/lib/language_operator/cli/errors/suggestions.rb +176 -0
  57. data/lib/language_operator/cli/formatters/code_formatter.rb +81 -0
  58. data/lib/language_operator/cli/formatters/log_formatter.rb +290 -0
  59. data/lib/language_operator/cli/formatters/progress_formatter.rb +53 -0
  60. data/lib/language_operator/cli/formatters/table_formatter.rb +179 -0
  61. data/lib/language_operator/cli/formatters/value_formatter.rb +113 -0
  62. data/lib/language_operator/cli/helpers/cluster_context.rb +62 -0
  63. data/lib/language_operator/cli/helpers/cluster_validator.rb +101 -0
  64. data/lib/language_operator/cli/helpers/editor_helper.rb +58 -0
  65. data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +167 -0
  66. data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +74 -0
  67. data/lib/language_operator/cli/helpers/schedule_builder.rb +108 -0
  68. data/lib/language_operator/cli/helpers/user_prompts.rb +69 -0
  69. data/lib/language_operator/cli/main.rb +232 -0
  70. data/lib/language_operator/cli/templates/tools/generic.yaml +66 -0
  71. data/lib/language_operator/cli/wizards/agent_wizard.rb +246 -0
  72. data/lib/language_operator/cli/wizards/quickstart_wizard.rb +588 -0
  73. data/lib/language_operator/client/base.rb +214 -0
  74. data/lib/language_operator/client/config.rb +136 -0
  75. data/lib/language_operator/client/cost_calculator.rb +37 -0
  76. data/lib/language_operator/client/mcp_connector.rb +123 -0
  77. data/lib/language_operator/client.rb +19 -0
  78. data/lib/language_operator/config/cluster_config.rb +101 -0
  79. data/lib/language_operator/config/tool_patterns.yaml +57 -0
  80. data/lib/language_operator/config/tool_registry.rb +96 -0
  81. data/lib/language_operator/config.rb +138 -0
  82. data/lib/language_operator/dsl/adapter.rb +124 -0
  83. data/lib/language_operator/dsl/agent_context.rb +90 -0
  84. data/lib/language_operator/dsl/agent_definition.rb +427 -0
  85. data/lib/language_operator/dsl/chat_endpoint_definition.rb +115 -0
  86. data/lib/language_operator/dsl/config.rb +119 -0
  87. data/lib/language_operator/dsl/context.rb +50 -0
  88. data/lib/language_operator/dsl/execution_context.rb +47 -0
  89. data/lib/language_operator/dsl/helpers.rb +109 -0
  90. data/lib/language_operator/dsl/http.rb +184 -0
  91. data/lib/language_operator/dsl/mcp_server_definition.rb +73 -0
  92. data/lib/language_operator/dsl/parameter_definition.rb +124 -0
  93. data/lib/language_operator/dsl/registry.rb +36 -0
  94. data/lib/language_operator/dsl/shell.rb +125 -0
  95. data/lib/language_operator/dsl/tool_definition.rb +112 -0
  96. data/lib/language_operator/dsl/webhook_authentication.rb +114 -0
  97. data/lib/language_operator/dsl/webhook_definition.rb +106 -0
  98. data/lib/language_operator/dsl/workflow_definition.rb +259 -0
  99. data/lib/language_operator/dsl.rb +160 -0
  100. data/lib/language_operator/errors.rb +60 -0
  101. data/lib/language_operator/kubernetes/client.rb +279 -0
  102. data/lib/language_operator/kubernetes/resource_builder.rb +194 -0
  103. data/lib/language_operator/loggable.rb +47 -0
  104. data/lib/language_operator/logger.rb +141 -0
  105. data/lib/language_operator/retry.rb +123 -0
  106. data/lib/language_operator/retryable.rb +132 -0
  107. data/lib/language_operator/tool_loader.rb +242 -0
  108. data/lib/language_operator/validators.rb +170 -0
  109. data/lib/language_operator/version.rb +1 -1
  110. data/lib/language_operator.rb +65 -3
  111. data/requirements/tasks/challenge.md +9 -0
  112. data/requirements/tasks/iterate.md +36 -0
  113. data/requirements/tasks/optimize.md +21 -0
  114. data/requirements/tasks/tag.md +5 -0
  115. data/test_agent_dsl.rb +108 -0
  116. metadata +503 -20
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'parameter_definition'
4
+ require_relative 'http'
5
+ require_relative 'shell'
6
+ require_relative 'helpers'
7
+
8
+ module LanguageOperator
9
+ module Dsl
10
+ # Tool definition for MCP tools
11
+ #
12
+ # Defines an MCP tool with parameters, description, and execution logic.
13
+ # Used within the DSL to create tools that can be registered and served.
14
+ #
15
+ # @example Define a simple tool
16
+ # tool "greet" do
17
+ # description "Greet a user by name"
18
+ #
19
+ # parameter :name do
20
+ # type :string
21
+ # required true
22
+ # description "Name to greet"
23
+ # end
24
+ #
25
+ # execute do |params|
26
+ # "Hello, #{params['name']}!"
27
+ # end
28
+ # end
29
+ class ToolDefinition
30
+ include LanguageOperator::Dsl::Helpers
31
+
32
+ # Provide access to HTTP and Shell helper classes as constants
33
+ HTTP = LanguageOperator::Dsl::HTTP
34
+ Shell = LanguageOperator::Dsl::Shell
35
+
36
+ attr_reader :name, :parameters, :execute_block
37
+
38
+ def initialize(name)
39
+ @name = name
40
+ @parameters = {}
41
+ @execute_block = nil
42
+ @description = nil
43
+ end
44
+
45
+ def description(val = nil)
46
+ return @description if val.nil?
47
+
48
+ @description = val
49
+ end
50
+
51
+ def parameter(name, &)
52
+ param = ParameterDefinition.new(name)
53
+ param.instance_eval(&) if block_given?
54
+ @parameters[name.to_s] = param
55
+ end
56
+
57
+ def execute(&block)
58
+ @execute_block = block
59
+ end
60
+
61
+ def call(params)
62
+ log_debug "Calling tool '#{@name}' with params: #{params.inspect}"
63
+
64
+ # Apply default values for missing optional parameters
65
+ @parameters.each do |name, param_def|
66
+ default_value = param_def.instance_variable_get(:@default)
67
+ params[name] = default_value if !params.key?(name) && !default_value.nil?
68
+
69
+ # Validate required parameters
70
+ raise ArgumentError, "Missing required parameter: #{name}" if param_def.required? && !params.key?(name)
71
+
72
+ # Validate parameter format if validator is set and value is present
73
+ if params.key?(name)
74
+ error = param_def.validate_value(params[name])
75
+ raise ArgumentError, error if error
76
+ end
77
+ end
78
+
79
+ # Call the execute block with parameters
80
+ result = @execute_block.call(params) if @execute_block
81
+
82
+ log_debug "Tool '#{@name}' completed: #{truncate_for_log(result)}"
83
+ result
84
+ end
85
+
86
+ def to_schema
87
+ {
88
+ 'name' => @name,
89
+ 'description' => @description,
90
+ 'inputSchema' => {
91
+ 'type' => 'object',
92
+ 'properties' => @parameters.transform_values(&:to_schema),
93
+ 'required' => @parameters.select { |_, p| p.required? }.keys
94
+ }
95
+ }
96
+ end
97
+
98
+ private
99
+
100
+ def log_debug(message)
101
+ puts "[DEBUG] #{message}" if ENV['DEBUG'] || ENV['MCP_DEBUG']
102
+ end
103
+
104
+ def truncate_for_log(text)
105
+ return text.inspect if text.nil?
106
+
107
+ str = text.to_s
108
+ str.length > 100 ? "#{str[0..100]}..." : str
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LanguageOperator
4
+ module Dsl
5
+ # Defines authentication configuration for webhooks
6
+ class WebhookAuthentication
7
+ attr_reader :type, :config
8
+
9
+ def initialize
10
+ @type = nil
11
+ @config = {}
12
+ end
13
+
14
+ # Verify HMAC signature (GitHub/Stripe style)
15
+ # @param header [String] Header name containing the signature
16
+ # @param secret [String] Secret key for HMAC
17
+ # @param algorithm [Symbol] Hash algorithm (:sha1, :sha256, :sha512)
18
+ # @param prefix [String, nil] Optional prefix to strip from signature (e.g., "sha256=")
19
+ def verify_signature(header:, secret:, algorithm: :sha256, prefix: nil)
20
+ if @current_methods
21
+ # Inside any_of/all_of - create child auth object
22
+ auth = WebhookAuthentication.new
23
+ auth.verify_signature(header: header, secret: secret, algorithm: algorithm, prefix: prefix)
24
+ @current_methods << auth
25
+ else
26
+ @type = :signature
27
+ @config[:header] = header
28
+ @config[:secret] = secret
29
+ @config[:algorithm] = algorithm
30
+ @config[:prefix] = prefix
31
+ end
32
+ end
33
+
34
+ # Verify API key from header
35
+ # @param header [String] Header name containing the API key
36
+ # @param key [String] Expected API key value
37
+ def verify_api_key(header:, key:)
38
+ if @current_methods
39
+ auth = WebhookAuthentication.new
40
+ auth.verify_api_key(header: header, key: key)
41
+ @current_methods << auth
42
+ else
43
+ @type = :api_key
44
+ @config[:header] = header
45
+ @config[:key] = key
46
+ end
47
+ end
48
+
49
+ # Verify bearer token
50
+ # @param token [String] Expected bearer token value
51
+ def verify_bearer_token(token:)
52
+ if @current_methods
53
+ auth = WebhookAuthentication.new
54
+ auth.verify_bearer_token(token: token)
55
+ @current_methods << auth
56
+ else
57
+ @type = :bearer_token
58
+ @config[:token] = token
59
+ end
60
+ end
61
+
62
+ # Verify basic auth credentials
63
+ # @param username [String] Expected username
64
+ # @param password [String] Expected password
65
+ def verify_basic_auth(username:, password:)
66
+ if @current_methods
67
+ auth = WebhookAuthentication.new
68
+ auth.verify_basic_auth(username: username, password: password)
69
+ @current_methods << auth
70
+ else
71
+ @type = :basic_auth
72
+ @config[:username] = username
73
+ @config[:password] = password
74
+ end
75
+ end
76
+
77
+ # Custom authentication callback
78
+ # @yield [context] Block receives request context hash
79
+ # @yieldreturn [Boolean] true if authenticated, false otherwise
80
+ def verify_custom(&block)
81
+ if @current_methods
82
+ auth = WebhookAuthentication.new
83
+ auth.verify_custom(&block)
84
+ @current_methods << auth
85
+ else
86
+ @type = :custom
87
+ @config[:callback] = block
88
+ end
89
+ end
90
+
91
+ # Allow multiple authentication methods (any can succeed)
92
+ def any_of(&block)
93
+ @type = :any_of
94
+ @config[:methods] = []
95
+ @current_methods = @config[:methods]
96
+ instance_eval(&block) if block
97
+ @current_methods = nil
98
+ # Restore type in case it was overwritten by method calls
99
+ @type = :any_of
100
+ end
101
+
102
+ # Allow multiple authentication methods (all must succeed)
103
+ def all_of(&block)
104
+ @type = :all_of
105
+ @config[:methods] = []
106
+ @current_methods = @config[:methods]
107
+ instance_eval(&block) if block
108
+ @current_methods = nil
109
+ # Restore type in case it was overwritten by method calls
110
+ @type = :all_of
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'webhook_authentication'
4
+
5
+ module LanguageOperator
6
+ module Dsl
7
+ # Webhook Definition
8
+ #
9
+ # Defines webhook endpoints for reactive agents.
10
+ #
11
+ # @example Define a webhook with authentication
12
+ # webhook "/github/pr-opened" do
13
+ # method :post
14
+ # authenticate do
15
+ # verify_signature header: "X-Hub-Signature-256",
16
+ # secret: ENV['GITHUB_WEBHOOK_SECRET'],
17
+ # algorithm: :sha256,
18
+ # prefix: "sha256="
19
+ # end
20
+ # on_request do |context|
21
+ # # Handle the webhook
22
+ # end
23
+ # end
24
+ class WebhookDefinition
25
+ attr_reader :path, :http_method, :handler, :authentication, :validations
26
+
27
+ # Initialize webhook definition
28
+ #
29
+ # @param path [String] URL path for the webhook
30
+ def initialize(path)
31
+ @path = path
32
+ @http_method = :post
33
+ @handler = nil
34
+ @authentication = nil
35
+ @validations = []
36
+ end
37
+
38
+ # Set HTTP method
39
+ #
40
+ # @param method_name [Symbol] HTTP method (:get, :post, :put, :delete, :patch)
41
+ # @return [void]
42
+ def method(method_name)
43
+ @http_method = method_name
44
+ end
45
+
46
+ # Define authentication for this webhook
47
+ #
48
+ # @yield Authentication configuration block
49
+ # @return [void]
50
+ def authenticate(&block)
51
+ @authentication = WebhookAuthentication.new
52
+ @authentication.instance_eval(&block) if block
53
+ end
54
+
55
+ # Require specific headers
56
+ #
57
+ # @param headers [Hash] Required headers and their expected values (nil = just check presence)
58
+ # @return [void]
59
+ def require_headers(headers)
60
+ @validations << { type: :headers, config: headers }
61
+ end
62
+
63
+ # Require specific content type
64
+ #
65
+ # @param content_type [String, Array<String>] Allowed content type(s)
66
+ # @return [void]
67
+ def require_content_type(*content_types)
68
+ @validations << { type: :content_type, config: content_types.flatten }
69
+ end
70
+
71
+ # Add custom validation
72
+ #
73
+ # @yield [context] Validation block
74
+ # @yieldreturn [Boolean, String] true if valid, or error message string if invalid
75
+ # @return [void]
76
+ def validate(&block)
77
+ @validations << { type: :custom, config: block }
78
+ end
79
+
80
+ # Define the request handler
81
+ #
82
+ # @yield [context] Request handler block
83
+ # @yieldparam context [Hash] Request context with :path, :method, :headers, :params, :body
84
+ # @return [void]
85
+ def on_request(&block)
86
+ @handler = block
87
+ end
88
+
89
+ # Register this webhook with a web server
90
+ #
91
+ # @param web_server [LanguageOperator::Agent::WebServer] Web server instance
92
+ # @return [void]
93
+ def register(web_server)
94
+ return unless @handler
95
+
96
+ web_server.register_route(
97
+ @path,
98
+ method: @http_method,
99
+ authentication: @authentication,
100
+ validations: @validations,
101
+ &@handler
102
+ )
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,259 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../logger'
4
+ require_relative '../loggable'
5
+
6
+ module LanguageOperator
7
+ module Dsl
8
+ # Workflow definition for agent execution
9
+ #
10
+ # Defines a series of steps that an agent executes to achieve objectives.
11
+ # Steps can depend on other steps, call tools, or perform LLM processing.
12
+ #
13
+ # @example Define a workflow
14
+ # workflow do
15
+ # step :search do
16
+ # tool "web_search"
17
+ # params query: "latest news"
18
+ # end
19
+ #
20
+ # step :summarize do
21
+ # depends_on :search
22
+ # prompt "Summarize: {search.output}"
23
+ # end
24
+ # end
25
+ class WorkflowDefinition
26
+ include LanguageOperator::Loggable
27
+
28
+ attr_reader :steps, :step_order
29
+
30
+ def initialize
31
+ @steps = {}
32
+ @step_order = []
33
+ end
34
+
35
+ # Define a workflow step
36
+ #
37
+ # @param name [Symbol] Step name
38
+ # @param tool [String, nil] Tool to use (optional)
39
+ # @param params [Hash] Tool parameters (optional)
40
+ # @param depends_on [Symbol, Array<Symbol>] Dependencies (optional)
41
+ # @yield Step definition block
42
+ # @return [void]
43
+ def step(name, tool: nil, params: {}, depends_on: nil, &block)
44
+ step_def = StepDefinition.new(name, logger: @logger)
45
+
46
+ if tool
47
+ step_def.tool(tool)
48
+ step_def.params(params) unless params.empty?
49
+ end
50
+
51
+ step_def.depends_on(depends_on) if depends_on
52
+
53
+ step_def.instance_eval(&block) if block
54
+ @steps[name] = step_def
55
+ @step_order << name
56
+ end
57
+
58
+ # Execute the workflow
59
+ #
60
+ # @param context [Object] Execution context
61
+ # @return [Hash] Results from each step
62
+ def execute(context = nil)
63
+ results = {}
64
+
65
+ logger.info('Executing workflow', step_count: @steps.size)
66
+
67
+ @step_order.each do |step_name|
68
+ step_def = @steps[step_name]
69
+
70
+ # Check dependencies
71
+ if step_def.dependencies.any?
72
+ logger.debug('Checking dependencies',
73
+ step: step_name,
74
+ dependencies: step_def.dependencies)
75
+ step_def.dependencies.each do |dep|
76
+ next if results.key?(dep)
77
+
78
+ logger.error('Dependency not satisfied',
79
+ step: step_name,
80
+ missing_dependency: dep)
81
+ raise "Step #{step_name} depends on #{dep}, but #{dep} has not been executed"
82
+ end
83
+ end
84
+
85
+ # Execute step
86
+ logger.info('Executing step',
87
+ step: step_name,
88
+ tool: step_def.tool_name,
89
+ has_prompt: !step_def.prompt_template.nil?)
90
+
91
+ result = logger.timed('Step execution') do
92
+ step_def.execute(results, context)
93
+ end
94
+
95
+ results[step_name] = result
96
+ logger.info('Step completed', step: step_name)
97
+ end
98
+
99
+ logger.info('Workflow execution completed', total_steps: @steps.size)
100
+ results
101
+ end
102
+
103
+ private
104
+
105
+ def logger_component
106
+ 'Workflow'
107
+ end
108
+ end
109
+
110
+ # Individual step definition
111
+ class StepDefinition
112
+ include LanguageOperator::Loggable
113
+
114
+ attr_reader :name, :dependencies, :tool_name, :tool_params, :prompt_template
115
+
116
+ def initialize(name, logger: nil)
117
+ @name = name
118
+ @tool_name = nil
119
+ @tool_params = {}
120
+ @prompt_template = nil
121
+ @dependencies = []
122
+ @execute_block = nil
123
+ @parent_logger = logger
124
+ end
125
+
126
+ # Set the tool to use
127
+ #
128
+ # @param name [String] Tool name
129
+ # @return [void]
130
+ def tool(name = nil)
131
+ return @tool_name if name.nil?
132
+
133
+ @tool_name = name
134
+ end
135
+
136
+ # Set tool parameters
137
+ #
138
+ # @param hash [Hash] Parameters
139
+ # @return [Hash] Current parameters
140
+ def params(hash = nil)
141
+ return @tool_params if hash.nil?
142
+
143
+ @tool_params = hash
144
+ end
145
+
146
+ # Set prompt template (for LLM processing)
147
+ #
148
+ # @param template [String] Prompt template
149
+ # @return [String] Current prompt
150
+ def prompt(template = nil)
151
+ return @prompt_template if template.nil?
152
+
153
+ @prompt_template = template
154
+ end
155
+
156
+ # Declare dependencies on other steps
157
+ #
158
+ # @param steps [Symbol, Array<Symbol>] Step names this depends on
159
+ # @return [Array<Symbol>] Current dependencies
160
+ def depends_on(*steps)
161
+ return @dependencies if steps.empty?
162
+
163
+ @dependencies = steps.flatten
164
+ end
165
+
166
+ # Define custom execution logic
167
+ #
168
+ # @yield Execution block
169
+ # @return [void]
170
+ def execute(&block)
171
+ @execute_block = block if block
172
+ end
173
+
174
+ # Execute this step
175
+ #
176
+ # @param results [Hash] Results from previous steps
177
+ # @param context [Object] Execution context
178
+ # @return [Object] Step result
179
+ def execute_step(results, context)
180
+ if @execute_block
181
+ # Custom execution logic
182
+ logger.debug('Executing custom logic', step: @name)
183
+ @execute_block.call(results, context)
184
+ elsif @tool_name
185
+ # Tool execution
186
+ params = interpolate_params(@tool_params, results)
187
+ logger.info('Calling tool',
188
+ step: @name,
189
+ tool: @tool_name,
190
+ params: params)
191
+ # In real implementation, this would call the actual tool
192
+ "Tool #{@tool_name} executed with #{params.inspect}"
193
+ elsif @prompt_template
194
+ # LLM processing
195
+ prompt = interpolate_template(@prompt_template, results)
196
+ logger.debug('LLM prompt',
197
+ step: @name,
198
+ prompt: prompt[0..200])
199
+ # In real implementation, this would call the LLM
200
+ "LLM processed: #{prompt}"
201
+ else
202
+ # No-op step
203
+ logger.debug('No execution logic defined', step: @name)
204
+ nil
205
+ end
206
+ end
207
+
208
+ alias execute execute_step
209
+
210
+ private
211
+
212
+ def logger
213
+ @parent_logger || super
214
+ end
215
+
216
+ def logger_component
217
+ "Step:#{@name}"
218
+ end
219
+
220
+ # Interpolate parameters with results from previous steps
221
+ #
222
+ # @param params [Hash] Parameter template
223
+ # @param results [Hash] Previous results
224
+ # @return [Hash] Interpolated parameters
225
+ def interpolate_params(params, results)
226
+ params.transform_values do |value|
227
+ if value.is_a?(String) && value.match?(/\{(\w+)\.(\w+)\}/)
228
+ # Replace {step.field} with actual value
229
+ value.gsub(/\{(\w+)\.(\w+)\}/) do
230
+ step_name = Regexp.last_match(1).to_sym
231
+ field = Regexp.last_match(2)
232
+ results.dig(step_name, field) || value
233
+ end
234
+ else
235
+ value
236
+ end
237
+ end
238
+ end
239
+
240
+ # Interpolate template string with results
241
+ #
242
+ # @param template [String] Template string
243
+ # @param results [Hash] Previous results
244
+ # @return [String] Interpolated string
245
+ def interpolate_template(template, results)
246
+ template.gsub(/\{(\w+)(?:\.(\w+))?\}/) do
247
+ step_name = Regexp.last_match(1).to_sym
248
+ field = Regexp.last_match(2)
249
+
250
+ if field
251
+ results.dig(step_name, field)&.to_s || "{#{step_name}.#{field}}"
252
+ else
253
+ results[step_name]&.to_s || "{#{step_name}}"
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
259
+ end