language-operator 0.1.31 → 0.1.35
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/.rubocop.yml +7 -8
- data/CHANGELOG.md +14 -0
- data/CI_STATUS.md +56 -0
- data/Gemfile.lock +2 -2
- data/Makefile +22 -6
- data/lib/language_operator/agent/base.rb +10 -6
- data/lib/language_operator/agent/executor.rb +19 -97
- data/lib/language_operator/agent/safety/ast_validator.rb +62 -43
- data/lib/language_operator/agent/safety/safe_executor.rb +27 -2
- data/lib/language_operator/agent/scheduler.rb +60 -0
- data/lib/language_operator/agent/task_executor.rb +548 -0
- data/lib/language_operator/agent.rb +90 -27
- data/lib/language_operator/cli/base_command.rb +117 -0
- data/lib/language_operator/cli/commands/agent.rb +339 -407
- data/lib/language_operator/cli/commands/cluster.rb +274 -290
- data/lib/language_operator/cli/commands/install.rb +110 -119
- data/lib/language_operator/cli/commands/model.rb +284 -184
- data/lib/language_operator/cli/commands/persona.rb +218 -284
- data/lib/language_operator/cli/commands/quickstart.rb +4 -5
- data/lib/language_operator/cli/commands/status.rb +31 -35
- data/lib/language_operator/cli/commands/system.rb +221 -233
- data/lib/language_operator/cli/commands/tool.rb +356 -422
- data/lib/language_operator/cli/commands/use.rb +19 -22
- data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +0 -18
- data/lib/language_operator/cli/wizards/quickstart_wizard.rb +0 -1
- data/lib/language_operator/client/config.rb +20 -21
- data/lib/language_operator/config.rb +115 -3
- data/lib/language_operator/constants.rb +54 -0
- data/lib/language_operator/dsl/agent_context.rb +7 -7
- data/lib/language_operator/dsl/agent_definition.rb +111 -26
- data/lib/language_operator/dsl/config.rb +30 -66
- data/lib/language_operator/dsl/main_definition.rb +114 -0
- data/lib/language_operator/dsl/schema.rb +84 -43
- data/lib/language_operator/dsl/task_definition.rb +315 -0
- data/lib/language_operator/dsl.rb +0 -1
- data/lib/language_operator/instrumentation/task_tracer.rb +285 -0
- data/lib/language_operator/logger.rb +4 -4
- data/lib/language_operator/synthesis_test_harness.rb +324 -0
- data/lib/language_operator/templates/examples/agent_synthesis.tmpl +26 -8
- data/lib/language_operator/templates/schema/CHANGELOG.md +26 -0
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +84 -42
- data/lib/language_operator/type_coercion.rb +250 -0
- data/lib/language_operator/ux/base.rb +81 -0
- data/lib/language_operator/ux/concerns/README.md +155 -0
- data/lib/language_operator/ux/concerns/headings.rb +90 -0
- data/lib/language_operator/ux/concerns/input_validation.rb +146 -0
- data/lib/language_operator/ux/concerns/provider_helpers.rb +167 -0
- data/lib/language_operator/ux/create_agent.rb +252 -0
- data/lib/language_operator/ux/create_model.rb +267 -0
- data/lib/language_operator/ux/quickstart.rb +594 -0
- data/lib/language_operator/version.rb +1 -1
- data/lib/language_operator.rb +2 -0
- data/requirements/ARCHITECTURE.md +1 -0
- data/requirements/SCRATCH.md +153 -0
- data/requirements/dsl.md +0 -0
- data/requirements/features +1 -0
- data/requirements/personas +1 -0
- data/requirements/proposals +1 -0
- data/requirements/tasks/iterate.md +14 -15
- data/requirements/tasks/optimize.md +13 -4
- data/synth/001/Makefile +90 -0
- data/synth/001/agent.rb +26 -0
- data/synth/001/agent.yaml +7 -0
- data/synth/001/output.log +44 -0
- data/synth/Makefile +39 -0
- data/synth/README.md +342 -0
- metadata +37 -10
- data/lib/language_operator/dsl/workflow_definition.rb +0 -259
- data/test_agent_dsl.rb +0 -108
|
@@ -1,259 +0,0 @@
|
|
|
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
|
data/test_agent_dsl.rb
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Quick test of the agent DSL without requiring full langop gem dependencies
|
|
5
|
-
|
|
6
|
-
require_relative 'lib/langop/dsl/agent_definition'
|
|
7
|
-
require_relative 'lib/langop/dsl/agent_context'
|
|
8
|
-
require_relative 'lib/langop/dsl/workflow_definition'
|
|
9
|
-
|
|
10
|
-
puts '=' * 70
|
|
11
|
-
puts 'Agent DSL Test'
|
|
12
|
-
puts '=' * 70
|
|
13
|
-
puts
|
|
14
|
-
|
|
15
|
-
# Create registry and context
|
|
16
|
-
registry = Langop::Dsl::AgentRegistry.new
|
|
17
|
-
context = Langop::Dsl::AgentContext.new(registry)
|
|
18
|
-
|
|
19
|
-
# Define an agent using the DSL (this is what the operator will synthesize)
|
|
20
|
-
context.agent 'kubernetes-news' do
|
|
21
|
-
description 'Daily Kubernetes news summarization agent'
|
|
22
|
-
|
|
23
|
-
# Persona (distilled by operator from LanguagePersona)
|
|
24
|
-
persona <<~PERSONA
|
|
25
|
-
You are a technical writer specializing in Kubernetes. When researching and
|
|
26
|
-
summarizing news, maintain a clear and precise tone, always cite your sources,
|
|
27
|
-
use proper technical terminology, and make complex topics accessible without
|
|
28
|
-
unnecessary jargon. Your summaries should be educational and well-structured.
|
|
29
|
-
PERSONA
|
|
30
|
-
|
|
31
|
-
# Schedule (extracted from: "once a day, preferably around lunchtime")
|
|
32
|
-
schedule '0 12 * * *'
|
|
33
|
-
|
|
34
|
-
# Objectives (extracted from instructions)
|
|
35
|
-
objectives [
|
|
36
|
-
'Search for recent Kubernetes news using web_search tool',
|
|
37
|
-
'Provide a concise summary of findings'
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
# Workflow (synthesized by operator)
|
|
41
|
-
workflow do
|
|
42
|
-
step :search do
|
|
43
|
-
tool 'web_search'
|
|
44
|
-
params query: 'Kubernetes news latest'
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
step :summarize do
|
|
48
|
-
depends_on :search
|
|
49
|
-
prompt 'Provide a concise summary of these Kubernetes news items: {search.output}'
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Constraints (inferred by operator)
|
|
54
|
-
constraints do
|
|
55
|
-
max_iterations 20
|
|
56
|
-
timeout '5m'
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Output (inferred from workspace settings)
|
|
60
|
-
output do
|
|
61
|
-
workspace 'summaries/kubernetes-{date}.md'
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Retrieve and display the agent
|
|
66
|
-
agent = registry.get('kubernetes-news')
|
|
67
|
-
|
|
68
|
-
if agent
|
|
69
|
-
puts '✅ Agent Definition Loaded'
|
|
70
|
-
puts
|
|
71
|
-
puts "Name: #{agent.name}"
|
|
72
|
-
puts "Description: #{agent.description}"
|
|
73
|
-
puts "Mode: #{agent.execution_mode}"
|
|
74
|
-
puts "Schedule: #{agent.schedule}"
|
|
75
|
-
puts
|
|
76
|
-
puts '📋 Persona:'
|
|
77
|
-
puts agent.persona.lines.map { |l| " #{l}" }.join
|
|
78
|
-
puts
|
|
79
|
-
puts "🎯 Objectives (#{agent.objectives.size}):"
|
|
80
|
-
agent.objectives.each_with_index do |obj, i|
|
|
81
|
-
puts " #{i + 1}. #{obj}"
|
|
82
|
-
end
|
|
83
|
-
puts
|
|
84
|
-
puts "⚙️ Workflow Steps (#{agent.workflow.steps.size}):"
|
|
85
|
-
agent.workflow.steps.each do |name, step|
|
|
86
|
-
deps = step.dependencies.empty? ? '' : " [depends on: #{step.dependencies.join(', ')}]"
|
|
87
|
-
tool_info = step.tool ? " (tool: #{step.tool})" : ''
|
|
88
|
-
prompt_info = step.prompt ? " (prompt: #{step.prompt[0..50]}...)" : ''
|
|
89
|
-
puts " - #{name}#{deps}#{tool_info}#{prompt_info}"
|
|
90
|
-
end
|
|
91
|
-
puts
|
|
92
|
-
puts '📊 Constraints:'
|
|
93
|
-
agent.constraints.each do |key, value|
|
|
94
|
-
puts " #{key}: #{value}"
|
|
95
|
-
end
|
|
96
|
-
puts
|
|
97
|
-
puts '📤 Output:'
|
|
98
|
-
agent.output_config.each do |key, value|
|
|
99
|
-
puts " #{key}: #{value}"
|
|
100
|
-
end
|
|
101
|
-
puts
|
|
102
|
-
puts '=' * 70
|
|
103
|
-
puts '✅ DSL Test Complete - Agent ready for execution!'
|
|
104
|
-
puts '=' * 70
|
|
105
|
-
else
|
|
106
|
-
puts '❌ Failed to load agent'
|
|
107
|
-
exit 1
|
|
108
|
-
end
|