language-operator 0.1.30 → 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 +49 -0
- data/CI_STATUS.md +56 -0
- data/Gemfile.lock +2 -2
- data/Makefile +28 -7
- data/Rakefile +29 -0
- data/docs/dsl/SCHEMA_VERSION.md +250 -0
- data/docs/dsl/agent-reference.md +13 -0
- 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 +39 -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 +351 -466
- data/lib/language_operator/cli/commands/cluster.rb +276 -256
- 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 +220 -289
- data/lib/language_operator/cli/commands/quickstart.rb +4 -5
- data/lib/language_operator/cli/commands/status.rb +36 -53
- data/lib/language_operator/cli/commands/system.rb +760 -0
- 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/formatters/code_formatter.rb +3 -7
- data/lib/language_operator/cli/formatters/log_formatter.rb +3 -5
- data/lib/language_operator/cli/formatters/progress_formatter.rb +3 -7
- data/lib/language_operator/cli/formatters/status_formatter.rb +37 -0
- data/lib/language_operator/cli/formatters/table_formatter.rb +10 -26
- data/lib/language_operator/cli/helpers/pastel_helper.rb +24 -0
- data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +0 -18
- data/lib/language_operator/cli/main.rb +4 -0
- 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 +1143 -0
- data/lib/language_operator/dsl/task_definition.rb +315 -0
- data/lib/language_operator/dsl.rb +1 -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/README.md +23 -0
- data/lib/language_operator/templates/examples/agent_synthesis.tmpl +133 -0
- data/lib/language_operator/templates/examples/persona_distillation.tmpl +19 -0
- data/lib/language_operator/templates/schema/.gitkeep +0 -0
- data/lib/language_operator/templates/schema/CHANGELOG.md +119 -0
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +306 -0
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +494 -0
- 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 +49 -18
- data/examples/README.md +0 -569
- data/examples/agent_example.rb +0 -86
- data/examples/chat_endpoint_agent.rb +0 -118
- data/examples/github_webhook_agent.rb +0 -171
- data/examples/mcp_agent.rb +0 -158
- data/examples/oauth_callback_agent.rb +0 -296
- data/examples/stripe_webhook_agent.rb +0 -219
- data/examples/webhook_agent.rb +0 -80
- data/lib/language_operator/dsl/workflow_definition.rb +0 -259
- data/test_agent_dsl.rb +0 -108
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'ruby_llm'
|
|
6
|
+
require_relative 'agent/safety/ast_validator'
|
|
7
|
+
|
|
8
|
+
module LanguageOperator
|
|
9
|
+
# SynthesisTestHarness replicates the Go operator's synthesis logic for local testing.
|
|
10
|
+
# This allows testing agent code generation without requiring a Kubernetes cluster.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# harness = LanguageOperator::SynthesisTestHarness.new
|
|
14
|
+
# code = harness.synthesize('synth/001/agent.yaml', model: 'claude-3-5-sonnet-20241022')
|
|
15
|
+
# File.write('agent.rb', code)
|
|
16
|
+
#
|
|
17
|
+
class SynthesisTestHarness
|
|
18
|
+
attr_reader :template_content, :model
|
|
19
|
+
|
|
20
|
+
def initialize(model: nil)
|
|
21
|
+
@model = model || detect_default_model
|
|
22
|
+
@synthesis_endpoint = ENV.fetch('SYNTHESIS_ENDPOINT', nil)
|
|
23
|
+
@synthesis_api_key = ENV['SYNTHESIS_API_KEY'] || 'dummy'
|
|
24
|
+
@template_path = File.join(__dir__, 'templates', 'examples', 'agent_synthesis.tmpl')
|
|
25
|
+
load_template
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Synthesize agent code from a LanguageAgent YAML file
|
|
29
|
+
#
|
|
30
|
+
# @param yaml_path [String] Path to LanguageAgent YAML file
|
|
31
|
+
# @param model [String, nil] LLM model to use (overrides default)
|
|
32
|
+
# @return [String] Generated Ruby DSL code
|
|
33
|
+
def synthesize(yaml_path, model: nil)
|
|
34
|
+
agent_spec = load_agent_spec(yaml_path)
|
|
35
|
+
|
|
36
|
+
# Build synthesis request
|
|
37
|
+
request = build_synthesis_request(agent_spec)
|
|
38
|
+
|
|
39
|
+
# Build prompt from template
|
|
40
|
+
prompt = build_prompt(request)
|
|
41
|
+
|
|
42
|
+
# Call LLM
|
|
43
|
+
response = call_llm(prompt, model: model || @model)
|
|
44
|
+
|
|
45
|
+
# Extract code from markdown
|
|
46
|
+
code = extract_code_from_markdown(response)
|
|
47
|
+
|
|
48
|
+
# Validate code
|
|
49
|
+
validate_code(code)
|
|
50
|
+
|
|
51
|
+
code
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def load_template
|
|
57
|
+
raise "Synthesis template not found at: #{@template_path}" unless File.exist?(@template_path)
|
|
58
|
+
|
|
59
|
+
@template_content = File.read(@template_path)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def load_agent_spec(yaml_path)
|
|
63
|
+
raise "Agent YAML file not found: #{yaml_path}" unless File.exist?(yaml_path)
|
|
64
|
+
|
|
65
|
+
yaml_content = File.read(yaml_path)
|
|
66
|
+
full_spec = YAML.safe_load(yaml_content, permitted_classes: [Symbol])
|
|
67
|
+
|
|
68
|
+
raise "Invalid kind: expected LanguageAgent, got #{full_spec['kind']}" unless full_spec['kind'] == 'LanguageAgent'
|
|
69
|
+
|
|
70
|
+
# Extract agent name from metadata and merge into spec
|
|
71
|
+
agent_spec = full_spec['spec'].dup
|
|
72
|
+
agent_spec['agentName'] = full_spec.dig('metadata', 'name') if full_spec.dig('metadata', 'name')
|
|
73
|
+
|
|
74
|
+
agent_spec
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def build_synthesis_request(agent_spec)
|
|
78
|
+
# NOTE: agent_spec is the 'spec' section from the YAML
|
|
79
|
+
# The agent name comes from metadata.name, which we need to extract from the full YAML
|
|
80
|
+
{
|
|
81
|
+
instructions: agent_spec['instructions'],
|
|
82
|
+
agent_name: agent_spec['agentName'] || 'test-agent', # Will be overridden by metadata.name
|
|
83
|
+
tools: agent_spec['toolRefs'] || [],
|
|
84
|
+
models: agent_spec['modelRefs'] || [],
|
|
85
|
+
persona: agent_spec['personaRefs']&.first || nil
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def build_prompt(request)
|
|
90
|
+
# Detect temporal intent from instructions
|
|
91
|
+
temporal_intent = detect_temporal_intent(request[:instructions])
|
|
92
|
+
|
|
93
|
+
# Format tools list
|
|
94
|
+
tools_list = format_list(request[:tools], 'No tools specified')
|
|
95
|
+
|
|
96
|
+
# Format models list
|
|
97
|
+
models_list = format_list(request[:models], 'No models specified')
|
|
98
|
+
|
|
99
|
+
# Build persona section
|
|
100
|
+
persona_section = ''
|
|
101
|
+
persona_section = " persona <<~PERSONA\n #{request[:persona]}\n PERSONA\n" if request[:persona]
|
|
102
|
+
|
|
103
|
+
# Build schedule section
|
|
104
|
+
schedule_section = ''
|
|
105
|
+
schedule_rules = ''
|
|
106
|
+
|
|
107
|
+
# Build constraints section
|
|
108
|
+
constraints_section = build_constraints_section(temporal_intent)
|
|
109
|
+
|
|
110
|
+
case temporal_intent
|
|
111
|
+
when :scheduled
|
|
112
|
+
schedule_section = "\n # Extract schedule from instructions (e.g., \"daily at noon\" -> \"0 12 * * *\")\n schedule \"CRON_EXPRESSION\""
|
|
113
|
+
schedule_rules = "2. Schedule detected - extract cron expression from instructions\n3. Set schedule block with appropriate cron expression\n4. Use high max_iterations for continuous scheduled operation"
|
|
114
|
+
when :oneshot
|
|
115
|
+
schedule_rules = "2. One-shot execution detected - agent will run a limited number of times\n3. Do NOT include a schedule block for one-shot agents"
|
|
116
|
+
when :continuous
|
|
117
|
+
schedule_rules = "2. No temporal intent detected - defaulting to continuous execution\n3. Do NOT include a schedule block unless explicitly mentioned\n4. Use high max_iterations for continuous operation"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Render template with variable substitution
|
|
121
|
+
rendered = @template_content.dup
|
|
122
|
+
|
|
123
|
+
# Handle conditional sections (simple implementation for {{if .ErrorContext}})
|
|
124
|
+
rendered.gsub!(/\{\{if \.ErrorContext\}\}.*?\{\{else\}\}/m, '')
|
|
125
|
+
rendered.gsub!('{{end}}', '')
|
|
126
|
+
|
|
127
|
+
# Replace variables
|
|
128
|
+
rendered.gsub!('{{.Instructions}}', request[:instructions])
|
|
129
|
+
rendered.gsub!('{{.ToolsList}}', tools_list)
|
|
130
|
+
rendered.gsub!('{{.ModelsList}}', models_list)
|
|
131
|
+
rendered.gsub!('{{.AgentName}}', request[:agent_name])
|
|
132
|
+
rendered.gsub!('{{.TemporalIntent}}', temporal_intent.to_s.capitalize)
|
|
133
|
+
rendered.gsub!('{{.PersonaSection}}', persona_section)
|
|
134
|
+
rendered.gsub!('{{.ScheduleSection}}', schedule_section)
|
|
135
|
+
rendered.gsub!('{{.ConstraintsSection}}', constraints_section)
|
|
136
|
+
rendered.gsub!('{{.ScheduleRules}}', schedule_rules)
|
|
137
|
+
|
|
138
|
+
rendered
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def detect_temporal_intent(instructions)
|
|
142
|
+
return :continuous if instructions.nil? || instructions.strip.empty?
|
|
143
|
+
|
|
144
|
+
lower = instructions.downcase
|
|
145
|
+
|
|
146
|
+
# One-shot indicators
|
|
147
|
+
oneshot_keywords = ['run once', 'one time', 'single time', 'execute once', 'just once']
|
|
148
|
+
return :oneshot if oneshot_keywords.any? { |keyword| lower.include?(keyword) }
|
|
149
|
+
|
|
150
|
+
# Schedule indicators
|
|
151
|
+
schedule_keywords = %w[every daily hourly weekly monthly cron schedule periodically]
|
|
152
|
+
return :scheduled if schedule_keywords.any? { |keyword| lower.include?(keyword) }
|
|
153
|
+
|
|
154
|
+
# Default to continuous
|
|
155
|
+
:continuous
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def build_constraints_section(temporal_intent)
|
|
159
|
+
case temporal_intent
|
|
160
|
+
when :oneshot
|
|
161
|
+
<<~CONSTRAINTS.chomp
|
|
162
|
+
# One-shot execution detected from instructions
|
|
163
|
+
constraints do
|
|
164
|
+
max_iterations 10
|
|
165
|
+
timeout "10m"
|
|
166
|
+
end
|
|
167
|
+
CONSTRAINTS
|
|
168
|
+
when :scheduled
|
|
169
|
+
<<~CONSTRAINTS.chomp
|
|
170
|
+
# Scheduled execution - high iteration limit for continuous operation
|
|
171
|
+
constraints do
|
|
172
|
+
max_iterations 999999
|
|
173
|
+
timeout "10m"
|
|
174
|
+
end
|
|
175
|
+
CONSTRAINTS
|
|
176
|
+
when :continuous
|
|
177
|
+
<<~CONSTRAINTS.chomp
|
|
178
|
+
# Continuous execution - no specific schedule or one-shot indicator found
|
|
179
|
+
constraints do
|
|
180
|
+
max_iterations 999999
|
|
181
|
+
timeout "10m"
|
|
182
|
+
end
|
|
183
|
+
CONSTRAINTS
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def format_list(items, default_text)
|
|
188
|
+
return default_text if items.nil? || items.empty?
|
|
189
|
+
|
|
190
|
+
items.map { |item| " - #{item}" }.join("\n")
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def call_llm(prompt, model:)
|
|
194
|
+
# Priority 1: Use SYNTHESIS_ENDPOINT if configured (OpenAI-compatible)
|
|
195
|
+
return call_openai_compatible(prompt, model) if @synthesis_endpoint
|
|
196
|
+
|
|
197
|
+
# Priority 2: Detect provider from model name
|
|
198
|
+
provider, api_key = detect_provider(model)
|
|
199
|
+
|
|
200
|
+
unless api_key
|
|
201
|
+
raise "No API key found. Set either:\n " \
|
|
202
|
+
"SYNTHESIS_ENDPOINT (for local/OpenAI-compatible)\n " \
|
|
203
|
+
"ANTHROPIC_API_KEY (for Claude)\n " \
|
|
204
|
+
'OPENAI_API_KEY (for GPT)'
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Configure RubyLLM for the provider
|
|
208
|
+
RubyLLM.configure do |config|
|
|
209
|
+
case provider
|
|
210
|
+
when :anthropic
|
|
211
|
+
config.anthropic_api_key = api_key
|
|
212
|
+
when :openai
|
|
213
|
+
config.openai_api_key = api_key
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Create chat and send message
|
|
218
|
+
chat = RubyLLM.chat(model: model, provider: provider)
|
|
219
|
+
response = chat.ask(prompt)
|
|
220
|
+
|
|
221
|
+
# Extract content
|
|
222
|
+
if response.respond_to?(:content)
|
|
223
|
+
response.content
|
|
224
|
+
elsif response.is_a?(Hash) && response.key?('content')
|
|
225
|
+
response['content']
|
|
226
|
+
elsif response.is_a?(String)
|
|
227
|
+
response
|
|
228
|
+
else
|
|
229
|
+
response.to_s
|
|
230
|
+
end
|
|
231
|
+
rescue StandardError => e
|
|
232
|
+
raise "LLM call failed: #{e.message}"
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def call_openai_compatible(prompt, model)
|
|
236
|
+
# Configure RubyLLM for OpenAI-compatible endpoint
|
|
237
|
+
RubyLLM.configure do |config|
|
|
238
|
+
config.openai_api_key = @synthesis_api_key
|
|
239
|
+
config.openai_api_base = @synthesis_endpoint
|
|
240
|
+
config.openai_use_system_role = true # Better compatibility with local models
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Create chat with OpenAI provider (will use configured endpoint)
|
|
244
|
+
chat = RubyLLM.chat(model: model, provider: :openai, assume_model_exists: true)
|
|
245
|
+
|
|
246
|
+
# Send message
|
|
247
|
+
response = chat.ask(prompt)
|
|
248
|
+
|
|
249
|
+
# Extract content
|
|
250
|
+
if response.respond_to?(:content)
|
|
251
|
+
response.content
|
|
252
|
+
elsif response.is_a?(Hash) && response.key?('content')
|
|
253
|
+
response['content']
|
|
254
|
+
elsif response.is_a?(String)
|
|
255
|
+
response
|
|
256
|
+
else
|
|
257
|
+
response.to_s
|
|
258
|
+
end
|
|
259
|
+
rescue StandardError => e
|
|
260
|
+
raise "OpenAI-compatible endpoint call failed: #{e.message}"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def detect_provider(model)
|
|
264
|
+
if model.start_with?('claude')
|
|
265
|
+
[:anthropic, ENV.fetch('ANTHROPIC_API_KEY', nil)]
|
|
266
|
+
elsif model.start_with?('gpt')
|
|
267
|
+
[:openai, ENV.fetch('OPENAI_API_KEY', nil)]
|
|
268
|
+
else
|
|
269
|
+
# Default to Anthropic
|
|
270
|
+
[:anthropic, ENV.fetch('ANTHROPIC_API_KEY', nil)]
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def detect_default_model
|
|
275
|
+
# Priority 1: Use SYNTHESIS_MODEL if configured
|
|
276
|
+
return ENV['SYNTHESIS_MODEL'] if ENV['SYNTHESIS_MODEL']
|
|
277
|
+
|
|
278
|
+
# Priority 2: Use cloud providers
|
|
279
|
+
if ENV['ANTHROPIC_API_KEY']
|
|
280
|
+
'claude-3-5-sonnet-20241022'
|
|
281
|
+
elsif ENV['OPENAI_API_KEY']
|
|
282
|
+
'gpt-4-turbo'
|
|
283
|
+
else
|
|
284
|
+
# Default to a reasonable model name for local endpoints
|
|
285
|
+
'mistralai/magistral-small-2509'
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def extract_code_from_markdown(content)
|
|
290
|
+
content = content.strip
|
|
291
|
+
|
|
292
|
+
# Try ```ruby first
|
|
293
|
+
if (match = content.match(/```ruby\n(.*?)```/m))
|
|
294
|
+
return match[1].strip
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Try generic ``` blocks
|
|
298
|
+
if (match = content.match(/```\n(.*?)```/m))
|
|
299
|
+
return match[1].strip
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# If no code blocks, return as-is and let validation catch it
|
|
303
|
+
content
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def validate_code(code)
|
|
307
|
+
# Basic checks
|
|
308
|
+
raise 'Empty code generated' if code.strip.empty?
|
|
309
|
+
raise "Code does not contain 'agent' definition" unless code.include?('agent ')
|
|
310
|
+
raise "Code does not require 'language_operator'" unless code.match?(/require ['"]language_operator['"]/)
|
|
311
|
+
|
|
312
|
+
# AST validation for security
|
|
313
|
+
validator = LanguageOperator::Agent::Safety::ASTValidator.new
|
|
314
|
+
violations = validator.validate(code, '(generated)')
|
|
315
|
+
|
|
316
|
+
unless violations.empty?
|
|
317
|
+
error_msgs = violations.map { |v| v[:message] }.join("\n")
|
|
318
|
+
raise "Security validation failed:\n#{error_msgs}"
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
true
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Templates Directory
|
|
2
|
+
|
|
3
|
+
This directory contains templates used by the Language Operator gem for various code generation and synthesis tasks.
|
|
4
|
+
|
|
5
|
+
## Directory Structure
|
|
6
|
+
|
|
7
|
+
- **`examples/`** - Example synthesis templates for agent and persona generation
|
|
8
|
+
- `agent_synthesis.tmpl` - Template for synthesizing agent definitions
|
|
9
|
+
- `persona_distillation.tmpl` - Template for distilling persona configurations
|
|
10
|
+
|
|
11
|
+
- **`schema/`** - JSON Schema definitions and validation templates
|
|
12
|
+
- Reserved for future schema artifacts and validation templates
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
Templates in this directory are used by:
|
|
17
|
+
- The `aictl system synthesis-template` command for managing synthesis templates
|
|
18
|
+
- Agent synthesis and persona distillation features
|
|
19
|
+
- Schema validation and code generation tools
|
|
20
|
+
|
|
21
|
+
## Template Format
|
|
22
|
+
|
|
23
|
+
Templates use a simple variable substitution format compatible with the synthesis engine. Variables are typically specified in the template header and replaced during synthesis.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
You are generating Ruby DSL code for an autonomous agent in a Kubernetes operator.
|
|
2
|
+
|
|
3
|
+
{{if .ErrorContext}}
|
|
4
|
+
## IMPORTANT: Self-Healing Synthesis - Attempt {{.AttemptNumber}}
|
|
5
|
+
|
|
6
|
+
The previous code synthesis encountered errors. Please analyze the errors below and generate CORRECTED code.
|
|
7
|
+
|
|
8
|
+
### Previous Synthesis Failures
|
|
9
|
+
|
|
10
|
+
{{if .ErrorContext.ValidationErrors}}
|
|
11
|
+
**Validation Errors** (detected during code generation):
|
|
12
|
+
{{range .ErrorContext.ValidationErrors}}
|
|
13
|
+
- {{.}}
|
|
14
|
+
{{end}}
|
|
15
|
+
{{end}}
|
|
16
|
+
|
|
17
|
+
{{if .ErrorContext.RuntimeErrors}}
|
|
18
|
+
**Runtime Errors** (detected during execution):
|
|
19
|
+
{{range .ErrorContext.RuntimeErrors}}
|
|
20
|
+
- Time: {{.Timestamp}}
|
|
21
|
+
- Type: {{.ErrorType}}
|
|
22
|
+
- Message: {{.ErrorMessage}}
|
|
23
|
+
{{if .StackTrace}}
|
|
24
|
+
- Stack Trace:
|
|
25
|
+
{{range .StackTrace}}
|
|
26
|
+
{{.}}
|
|
27
|
+
{{end}}
|
|
28
|
+
{{end}}
|
|
29
|
+
- Exit Code: {{.ContainerExitCode}}
|
|
30
|
+
{{end}}
|
|
31
|
+
{{end}}
|
|
32
|
+
|
|
33
|
+
{{if .ErrorContext.LastCrashLog}}
|
|
34
|
+
**Last Container Logs** (before crash):
|
|
35
|
+
```
|
|
36
|
+
{{.ErrorContext.LastCrashLog}}
|
|
37
|
+
```
|
|
38
|
+
{{end}}
|
|
39
|
+
|
|
40
|
+
### Your Task
|
|
41
|
+
|
|
42
|
+
1. Carefully analyze each error above
|
|
43
|
+
2. Identify the root cause of the failure
|
|
44
|
+
3. Generate CORRECTED Ruby DSL code that addresses ALL errors
|
|
45
|
+
4. Ensure the code:
|
|
46
|
+
- Fixes the specific errors mentioned
|
|
47
|
+
- Uses only available tools: {{.ToolsList}}
|
|
48
|
+
- Uses only available models: {{.ModelsList}}
|
|
49
|
+
- Follows the Language Operator DSL syntax exactly
|
|
50
|
+
- Does NOT use any dangerous Ruby methods (system, eval, etc.)
|
|
51
|
+
|
|
52
|
+
This is attempt {{.AttemptNumber}} of {{.MaxAttempts}}. The user is counting on you to get it right!
|
|
53
|
+
|
|
54
|
+
{{if .LastKnownGoodCode}}
|
|
55
|
+
### Last Known Working Code (for reference)
|
|
56
|
+
```ruby
|
|
57
|
+
{{.LastKnownGoodCode}}
|
|
58
|
+
```
|
|
59
|
+
{{end}}
|
|
60
|
+
|
|
61
|
+
{{else}}
|
|
62
|
+
## Agent Synthesis Request
|
|
63
|
+
{{end}}
|
|
64
|
+
|
|
65
|
+
**User Instructions:**
|
|
66
|
+
{{.Instructions}}
|
|
67
|
+
|
|
68
|
+
**Available Tools:**
|
|
69
|
+
{{.ToolsList}}
|
|
70
|
+
|
|
71
|
+
**Available Models:**
|
|
72
|
+
{{.ModelsList}}
|
|
73
|
+
|
|
74
|
+
**Agent Name:** {{.AgentName}}
|
|
75
|
+
|
|
76
|
+
**Detected Temporal Intent:** {{.TemporalIntent}}
|
|
77
|
+
|
|
78
|
+
**Runtime Context:**
|
|
79
|
+
- All agent messages and output are automatically logged to stdout
|
|
80
|
+
- Agents have access to a workspace directory for file operations
|
|
81
|
+
- LLM responses are captured and available in agent execution context
|
|
82
|
+
|
|
83
|
+
Generate Ruby DSL code using this exact format (wrapped in triple-backticks with ruby):
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
require 'language_operator'
|
|
87
|
+
|
|
88
|
+
agent "{{.AgentName}}" do
|
|
89
|
+
description "Brief description extracted from instructions"
|
|
90
|
+
{{.PersonaSection}}{{.ScheduleSection}}
|
|
91
|
+
# Extract objectives from instructions
|
|
92
|
+
objectives [
|
|
93
|
+
"First objective",
|
|
94
|
+
"Second objective"
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
# REQUIRED: Define workflow with at least one step
|
|
98
|
+
workflow do
|
|
99
|
+
# Use tools when available
|
|
100
|
+
step :step_name, tool: "tool_name", params: {key: "value"}
|
|
101
|
+
|
|
102
|
+
# Or use execute blocks for custom Ruby code (simple logging, calculations, etc.)
|
|
103
|
+
step :custom_step do
|
|
104
|
+
execute do
|
|
105
|
+
puts "Custom output from Ruby code"
|
|
106
|
+
{ result: "done" }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Chain steps with dependencies if needed
|
|
111
|
+
step :another_step, depends_on: :step_name
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
{{.ConstraintsSection}}
|
|
115
|
+
|
|
116
|
+
# Output configuration (if workspace enabled)
|
|
117
|
+
output do
|
|
118
|
+
workspace "results/output.txt"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Rules:**
|
|
124
|
+
1. Generate ONLY the Ruby code within triple-backticks, no explanations before or after
|
|
125
|
+
{{.ScheduleRules}}
|
|
126
|
+
5. Break down instructions into clear, actionable objectives
|
|
127
|
+
6. REQUIRED: Always include a workflow block with at least one step (even for simple single-action agents)
|
|
128
|
+
7. For simple tasks (logging, calculations), use a single step with an execute block containing Ruby code
|
|
129
|
+
8. For complex tasks, use multiple steps with tools or execute blocks
|
|
130
|
+
9. Use available tools in workflow steps when tools are provided
|
|
131
|
+
10. Use the agent name: "{{.AgentName}}"
|
|
132
|
+
|
|
133
|
+
Generate the code now:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Distill this persona into a single concise paragraph for an AI agent.
|
|
2
|
+
|
|
3
|
+
**Persona Details:**
|
|
4
|
+
Name: {{.PersonaName}}
|
|
5
|
+
Description: {{.PersonaDescription}}
|
|
6
|
+
System Prompt: {{.PersonaSystemPrompt}}
|
|
7
|
+
Tone: {{.PersonaTone}}
|
|
8
|
+
Language: {{.PersonaLanguage}}
|
|
9
|
+
|
|
10
|
+
**Agent Context:**
|
|
11
|
+
Goal: {{.AgentInstructions}}
|
|
12
|
+
Available Tools: {{.AgentTools}}
|
|
13
|
+
|
|
14
|
+
Generate a single paragraph (2-4 sentences) that captures the essence of this persona
|
|
15
|
+
in the context of the agent's goal. Focus on tone, expertise, and key behaviors.
|
|
16
|
+
|
|
17
|
+
Output ONLY the distilled persona paragraph, nothing else.
|
|
18
|
+
|
|
19
|
+
Distilled persona:
|
|
File without changes
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Schema Changelog
|
|
2
|
+
|
|
3
|
+
This file tracks changes to the Language Operator Agent DSL schema.
|
|
4
|
+
|
|
5
|
+
## Schema Versioning
|
|
6
|
+
|
|
7
|
+
The schema version is tied directly to the gem version and follows [Semantic Versioning](https://semver.org/):
|
|
8
|
+
|
|
9
|
+
- **MAJOR** version: Breaking changes to DSL structure or behavior
|
|
10
|
+
- **MINOR** version: New features, backward-compatible additions
|
|
11
|
+
- **PATCH** version: Bug fixes, documentation improvements
|
|
12
|
+
|
|
13
|
+
## Version History
|
|
14
|
+
|
|
15
|
+
### 0.1.34 (2025-11-14)
|
|
16
|
+
|
|
17
|
+
**DSL v1: Task/Main Primitives Added**
|
|
18
|
+
|
|
19
|
+
This release adds support for the new DSL v1 pattern with task/main primitives while maintaining backward compatibility with the workflow/step pattern.
|
|
20
|
+
|
|
21
|
+
**New Features:**
|
|
22
|
+
- Added `task()` DSL method to AgentDefinition for defining organic functions
|
|
23
|
+
- Task definitions support neural (instructions), symbolic (code block), and hybrid implementations
|
|
24
|
+
- Tasks stored in `@tasks` hash on AgentDefinition
|
|
25
|
+
- Full input/output schema validation via TaskDefinition
|
|
26
|
+
|
|
27
|
+
**Improvements:**
|
|
28
|
+
- Added deprecation warning to `workflow()` method
|
|
29
|
+
- Updated schema to include task definitions
|
|
30
|
+
- Added comprehensive test coverage for task registration
|
|
31
|
+
|
|
32
|
+
**Deprecated:**
|
|
33
|
+
- `workflow` and `step` pattern (use `task` and `main` instead)
|
|
34
|
+
- Migration guide available in requirements/proposals/dsl-v1.md
|
|
35
|
+
|
|
36
|
+
**Backward Compatibility:**
|
|
37
|
+
- Existing workflow-based agents continue to work
|
|
38
|
+
- Both task and workflow can coexist in same agent during migration
|
|
39
|
+
- No breaking changes to existing code
|
|
40
|
+
|
|
41
|
+
### 0.1.30 (2025-11-12)
|
|
42
|
+
|
|
43
|
+
**Initial schema artifact generation**
|
|
44
|
+
|
|
45
|
+
This is the first release with auto-generated schema artifacts included in the gem package.
|
|
46
|
+
|
|
47
|
+
**Schema Features:**
|
|
48
|
+
- Complete JSON Schema v7 for Agent DSL
|
|
49
|
+
- OpenAPI 3.0.3 specification for agent HTTP endpoints
|
|
50
|
+
- Agent configuration properties (name, description, persona, mode, schedule, objectives)
|
|
51
|
+
- Workflow definitions with step dependencies
|
|
52
|
+
- Constraint definitions (budgets, rate limits, timeouts)
|
|
53
|
+
- Output destinations (workspace, Slack, email)
|
|
54
|
+
- Webhook definitions with authentication
|
|
55
|
+
- MCP server configuration
|
|
56
|
+
- Chat endpoint configuration (OpenAI-compatible)
|
|
57
|
+
- Tool and parameter definitions
|
|
58
|
+
|
|
59
|
+
**API Endpoints Documented:**
|
|
60
|
+
- `GET /health` - Health check
|
|
61
|
+
- `GET /ready` - Readiness check
|
|
62
|
+
- `POST /v1/chat/completions` - OpenAI-compatible chat endpoint
|
|
63
|
+
- `GET /v1/models` - List available models
|
|
64
|
+
|
|
65
|
+
**Safe Methods:**
|
|
66
|
+
- Agent DSL methods validated via `Agent::Safety::ASTValidator`
|
|
67
|
+
- Tool DSL methods for parameter definitions
|
|
68
|
+
- Helper methods for HTTP, Shell, validation, and utilities
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Future Versions
|
|
73
|
+
|
|
74
|
+
### Template for New Entries
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
### X.Y.Z (YYYY-MM-DD)
|
|
78
|
+
|
|
79
|
+
**Summary of changes**
|
|
80
|
+
|
|
81
|
+
**Breaking Changes:**
|
|
82
|
+
- Description of any breaking changes
|
|
83
|
+
|
|
84
|
+
**New Features:**
|
|
85
|
+
- New DSL methods or capabilities added
|
|
86
|
+
- New endpoint specifications
|
|
87
|
+
|
|
88
|
+
**Improvements:**
|
|
89
|
+
- Schema validation enhancements
|
|
90
|
+
- Documentation updates
|
|
91
|
+
|
|
92
|
+
**Bug Fixes:**
|
|
93
|
+
- Schema corrections
|
|
94
|
+
- Type definition fixes
|
|
95
|
+
|
|
96
|
+
**Deprecated:**
|
|
97
|
+
- Features marked for removal in future versions
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Schema Validation
|
|
103
|
+
|
|
104
|
+
The schema can be used for:
|
|
105
|
+
- **Template Validation** - Ensuring synthesized agent code is valid
|
|
106
|
+
- **Documentation Generation** - Auto-generating reference docs
|
|
107
|
+
- **IDE Support** - Providing autocomplete and IntelliSense
|
|
108
|
+
- **CLI Introspection** - Runtime validation of agent definitions
|
|
109
|
+
|
|
110
|
+
## Schema Artifacts
|
|
111
|
+
|
|
112
|
+
Generated artifacts are stored in this directory:
|
|
113
|
+
- `agent_dsl_schema.json` - JSON Schema v7 specification
|
|
114
|
+
- `agent_dsl_openapi.yaml` - OpenAPI 3.0.3 specification
|
|
115
|
+
|
|
116
|
+
These are regenerated automatically during the build process via:
|
|
117
|
+
```bash
|
|
118
|
+
rake schema:generate
|
|
119
|
+
```
|