language-operator 0.0.1 → 0.1.31

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +125 -0
  3. data/CHANGELOG.md +88 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +284 -0
  6. data/LICENSE +229 -21
  7. data/Makefile +82 -0
  8. data/README.md +3 -11
  9. data/Rakefile +63 -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/SCHEMA_VERSION.md +250 -0
  16. data/docs/dsl/agent-reference.md +604 -0
  17. data/docs/dsl/best-practices.md +1078 -0
  18. data/docs/dsl/chat-endpoints.md +895 -0
  19. data/docs/dsl/constraints.md +671 -0
  20. data/docs/dsl/mcp-integration.md +1177 -0
  21. data/docs/dsl/webhooks.md +932 -0
  22. data/docs/dsl/workflows.md +744 -0
  23. data/lib/language_operator/agent/base.rb +110 -0
  24. data/lib/language_operator/agent/executor.rb +440 -0
  25. data/lib/language_operator/agent/instrumentation.rb +54 -0
  26. data/lib/language_operator/agent/metrics_tracker.rb +183 -0
  27. data/lib/language_operator/agent/safety/ast_validator.rb +272 -0
  28. data/lib/language_operator/agent/safety/audit_logger.rb +104 -0
  29. data/lib/language_operator/agent/safety/budget_tracker.rb +175 -0
  30. data/lib/language_operator/agent/safety/content_filter.rb +93 -0
  31. data/lib/language_operator/agent/safety/manager.rb +207 -0
  32. data/lib/language_operator/agent/safety/rate_limiter.rb +150 -0
  33. data/lib/language_operator/agent/safety/safe_executor.rb +127 -0
  34. data/lib/language_operator/agent/scheduler.rb +183 -0
  35. data/lib/language_operator/agent/telemetry.rb +116 -0
  36. data/lib/language_operator/agent/web_server.rb +610 -0
  37. data/lib/language_operator/agent/webhook_authenticator.rb +226 -0
  38. data/lib/language_operator/agent.rb +149 -0
  39. data/lib/language_operator/cli/commands/agent.rb +1205 -0
  40. data/lib/language_operator/cli/commands/cluster.rb +371 -0
  41. data/lib/language_operator/cli/commands/install.rb +404 -0
  42. data/lib/language_operator/cli/commands/model.rb +266 -0
  43. data/lib/language_operator/cli/commands/persona.rb +393 -0
  44. data/lib/language_operator/cli/commands/quickstart.rb +22 -0
  45. data/lib/language_operator/cli/commands/status.rb +143 -0
  46. data/lib/language_operator/cli/commands/system.rb +772 -0
  47. data/lib/language_operator/cli/commands/tool.rb +537 -0
  48. data/lib/language_operator/cli/commands/use.rb +47 -0
  49. data/lib/language_operator/cli/errors/handler.rb +180 -0
  50. data/lib/language_operator/cli/errors/suggestions.rb +176 -0
  51. data/lib/language_operator/cli/formatters/code_formatter.rb +77 -0
  52. data/lib/language_operator/cli/formatters/log_formatter.rb +288 -0
  53. data/lib/language_operator/cli/formatters/progress_formatter.rb +49 -0
  54. data/lib/language_operator/cli/formatters/status_formatter.rb +37 -0
  55. data/lib/language_operator/cli/formatters/table_formatter.rb +163 -0
  56. data/lib/language_operator/cli/formatters/value_formatter.rb +113 -0
  57. data/lib/language_operator/cli/helpers/cluster_context.rb +62 -0
  58. data/lib/language_operator/cli/helpers/cluster_validator.rb +101 -0
  59. data/lib/language_operator/cli/helpers/editor_helper.rb +58 -0
  60. data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +167 -0
  61. data/lib/language_operator/cli/helpers/pastel_helper.rb +24 -0
  62. data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +74 -0
  63. data/lib/language_operator/cli/helpers/schedule_builder.rb +108 -0
  64. data/lib/language_operator/cli/helpers/user_prompts.rb +69 -0
  65. data/lib/language_operator/cli/main.rb +236 -0
  66. data/lib/language_operator/cli/templates/tools/generic.yaml +66 -0
  67. data/lib/language_operator/cli/wizards/agent_wizard.rb +246 -0
  68. data/lib/language_operator/cli/wizards/quickstart_wizard.rb +588 -0
  69. data/lib/language_operator/client/base.rb +214 -0
  70. data/lib/language_operator/client/config.rb +136 -0
  71. data/lib/language_operator/client/cost_calculator.rb +37 -0
  72. data/lib/language_operator/client/mcp_connector.rb +123 -0
  73. data/lib/language_operator/client.rb +19 -0
  74. data/lib/language_operator/config/cluster_config.rb +101 -0
  75. data/lib/language_operator/config/tool_patterns.yaml +57 -0
  76. data/lib/language_operator/config/tool_registry.rb +96 -0
  77. data/lib/language_operator/config.rb +138 -0
  78. data/lib/language_operator/dsl/adapter.rb +124 -0
  79. data/lib/language_operator/dsl/agent_context.rb +90 -0
  80. data/lib/language_operator/dsl/agent_definition.rb +427 -0
  81. data/lib/language_operator/dsl/chat_endpoint_definition.rb +115 -0
  82. data/lib/language_operator/dsl/config.rb +119 -0
  83. data/lib/language_operator/dsl/context.rb +50 -0
  84. data/lib/language_operator/dsl/execution_context.rb +47 -0
  85. data/lib/language_operator/dsl/helpers.rb +109 -0
  86. data/lib/language_operator/dsl/http.rb +184 -0
  87. data/lib/language_operator/dsl/mcp_server_definition.rb +73 -0
  88. data/lib/language_operator/dsl/parameter_definition.rb +124 -0
  89. data/lib/language_operator/dsl/registry.rb +36 -0
  90. data/lib/language_operator/dsl/schema.rb +1102 -0
  91. data/lib/language_operator/dsl/shell.rb +125 -0
  92. data/lib/language_operator/dsl/tool_definition.rb +112 -0
  93. data/lib/language_operator/dsl/webhook_authentication.rb +114 -0
  94. data/lib/language_operator/dsl/webhook_definition.rb +106 -0
  95. data/lib/language_operator/dsl/workflow_definition.rb +259 -0
  96. data/lib/language_operator/dsl.rb +161 -0
  97. data/lib/language_operator/errors.rb +60 -0
  98. data/lib/language_operator/kubernetes/client.rb +279 -0
  99. data/lib/language_operator/kubernetes/resource_builder.rb +194 -0
  100. data/lib/language_operator/loggable.rb +47 -0
  101. data/lib/language_operator/logger.rb +141 -0
  102. data/lib/language_operator/retry.rb +123 -0
  103. data/lib/language_operator/retryable.rb +132 -0
  104. data/lib/language_operator/templates/README.md +23 -0
  105. data/lib/language_operator/templates/examples/agent_synthesis.tmpl +115 -0
  106. data/lib/language_operator/templates/examples/persona_distillation.tmpl +19 -0
  107. data/lib/language_operator/templates/schema/.gitkeep +0 -0
  108. data/lib/language_operator/templates/schema/CHANGELOG.md +93 -0
  109. data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +306 -0
  110. data/lib/language_operator/templates/schema/agent_dsl_schema.json +452 -0
  111. data/lib/language_operator/tool_loader.rb +242 -0
  112. data/lib/language_operator/validators.rb +170 -0
  113. data/lib/language_operator/version.rb +1 -1
  114. data/lib/language_operator.rb +65 -3
  115. data/requirements/tasks/challenge.md +9 -0
  116. data/requirements/tasks/iterate.md +36 -0
  117. data/requirements/tasks/optimize.md +21 -0
  118. data/requirements/tasks/tag.md +5 -0
  119. data/test_agent_dsl.rb +108 -0
  120. metadata +507 -20
@@ -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
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+ require_relative 'dsl/tool_definition'
5
+ require_relative 'dsl/parameter_definition'
6
+ require_relative 'dsl/registry'
7
+ require_relative 'dsl/adapter'
8
+ require_relative 'dsl/config'
9
+ require_relative 'dsl/helpers'
10
+ require_relative 'dsl/http'
11
+ require_relative 'dsl/shell'
12
+ require_relative 'dsl/context'
13
+ require_relative 'dsl/execution_context'
14
+ require_relative 'dsl/agent_definition'
15
+ require_relative 'dsl/agent_context'
16
+ require_relative 'dsl/workflow_definition'
17
+ require_relative 'dsl/schema'
18
+ require_relative 'agent/safety/ast_validator'
19
+ require_relative 'agent/safety/safe_executor'
20
+
21
+ module LanguageOperator
22
+ # DSL for defining MCP tools and autonomous agents
23
+ #
24
+ # Provides a clean, Ruby-like DSL for defining tools that can be served
25
+ # via the Model Context Protocol (MCP) and agents that can execute autonomously.
26
+ #
27
+ # @example Define a tool
28
+ # LanguageOperator::Dsl.define do
29
+ # tool "greet" do
30
+ # description "Greet a user by name"
31
+ #
32
+ # parameter :name do
33
+ # type :string
34
+ # required true
35
+ # description "Name to greet"
36
+ # end
37
+ #
38
+ # execute do |params|
39
+ # "Hello, #{params['name']}!"
40
+ # end
41
+ # end
42
+ # end
43
+ #
44
+ # @example Access tools
45
+ # registry = LanguageOperator::Dsl.registry
46
+ # tool = registry.get("greet")
47
+ # result = tool.call({"name" => "Alice"})
48
+ module Dsl
49
+ class << self
50
+ # Global registry for tools
51
+ #
52
+ # @return [Registry] The global tool registry
53
+ def registry
54
+ @registry ||= Registry.new
55
+ end
56
+
57
+ # Global registry for agents
58
+ #
59
+ # @return [AgentRegistry] The global agent registry
60
+ def agent_registry
61
+ @agent_registry ||= AgentRegistry.new
62
+ end
63
+
64
+ # Define tools using the DSL
65
+ #
66
+ # @yield Block containing tool definitions
67
+ # @return [Registry] The global registry with defined tools
68
+ #
69
+ # @example
70
+ # LanguageOperator::Dsl.define do
71
+ # tool "example" do
72
+ # # ...
73
+ # end
74
+ # end
75
+ def define(&)
76
+ context = Context.new(registry)
77
+ context.instance_eval(&)
78
+ registry
79
+ end
80
+
81
+ # Define agents using the DSL
82
+ #
83
+ # @yield Block containing agent definitions
84
+ # @return [AgentRegistry] The global agent registry
85
+ #
86
+ # @example
87
+ # LanguageOperator::Dsl.define_agents do
88
+ # agent "news-summarizer" do
89
+ # # ...
90
+ # end
91
+ # end
92
+ def define_agents(&)
93
+ context = AgentContext.new(agent_registry)
94
+ context.instance_eval(&)
95
+ agent_registry
96
+ end
97
+
98
+ # Load tools from a file
99
+ #
100
+ # @param file_path [String] Path to the tool definition file
101
+ # @return [Registry] The global registry with loaded tools
102
+ #
103
+ # @example
104
+ # LanguageOperator::Dsl.load_file("mcp/tools.rb")
105
+ def load_file(file_path)
106
+ code = File.read(file_path)
107
+ context = Context.new(registry)
108
+
109
+ # Execute in sandbox with validation
110
+ executor = Agent::Safety::SafeExecutor.new(context)
111
+ executor.eval(code, file_path)
112
+
113
+ registry
114
+ end
115
+
116
+ # Load agents from a file
117
+ #
118
+ # @param file_path [String] Path to the agent definition file
119
+ # @return [AgentRegistry] The global agent registry
120
+ #
121
+ # @example
122
+ # LanguageOperator::Dsl.load_agent_file("agents/news-summarizer.rb")
123
+ def load_agent_file(file_path)
124
+ code = File.read(file_path)
125
+ context = AgentContext.new(agent_registry)
126
+
127
+ # Execute in sandbox with validation
128
+ executor = Agent::Safety::SafeExecutor.new(context)
129
+ executor.eval(code, file_path)
130
+
131
+ agent_registry
132
+ end
133
+
134
+ # Clear all defined tools
135
+ #
136
+ # @return [void]
137
+ def clear!
138
+ registry.clear
139
+ end
140
+
141
+ # Clear all defined agents
142
+ #
143
+ # @return [void]
144
+ def clear_agents!
145
+ agent_registry.clear
146
+ end
147
+
148
+ # Create an MCP server from the defined tools
149
+ #
150
+ # @param server_name [String] Name of the MCP server
151
+ # @param server_context [Hash] Additional context for the server
152
+ # @return [MCP::Server] The MCP server instance
153
+ #
154
+ # @example
155
+ # server = LanguageOperator::Dsl.create_server(server_name: "my-tools")
156
+ def create_server(server_name: 'langop-tools', server_context: {})
157
+ Adapter.create_mcp_server(registry, server_name: server_name, server_context: server_context)
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LanguageOperator
4
+ # Standardized error formatting module for consistent error messages across tools
5
+ module Errors
6
+ # Resource not found error
7
+ # @param resource_type [String] Type of resource (e.g., "Pod", "LanguageAgent")
8
+ # @param identifier [String] Resource identifier (name, ID, etc.)
9
+ # @return [String] Formatted error message
10
+ def self.not_found(resource_type, identifier)
11
+ "Error: #{resource_type} not found - #{identifier}"
12
+ end
13
+
14
+ # Access denied error
15
+ # @param context [String] Additional context (default: "check RBAC permissions")
16
+ # @return [String] Formatted error message
17
+ def self.access_denied(context = 'check RBAC permissions')
18
+ "Error: Access denied - #{context}"
19
+ end
20
+
21
+ # Invalid JSON parameter error
22
+ # @param param_name [String] Name of the parameter
23
+ # @return [String] Formatted error message
24
+ def self.invalid_json(param_name)
25
+ "Error: Invalid JSON in #{param_name} parameter"
26
+ end
27
+
28
+ # Missing configuration error
29
+ # @param missing_vars [String, Array<String>] Missing variable(s)
30
+ # @return [String] Formatted error message
31
+ def self.missing_config(missing_vars)
32
+ vars = Array(missing_vars).join(', ')
33
+ "Error: Missing configuration: #{vars}"
34
+ end
35
+
36
+ # Invalid parameter value error
37
+ # @param param_name [String] Parameter name
38
+ # @param value [String] Invalid value
39
+ # @param expected [String] Expected format/value
40
+ # @return [String] Formatted error message
41
+ def self.invalid_parameter(param_name, value, expected)
42
+ "Error: Invalid #{param_name} '#{value}'. Expected: #{expected}"
43
+ end
44
+
45
+ # Generic operation failed error
46
+ # @param operation [String] Operation that failed
47
+ # @param reason [String] Reason for failure
48
+ # @return [String] Formatted error message
49
+ def self.operation_failed(operation, reason)
50
+ "Error: #{operation} failed - #{reason}"
51
+ end
52
+
53
+ # Empty/missing required field error
54
+ # @param field_name [String] Name of the field
55
+ # @return [String] Formatted error message
56
+ def self.empty_field(field_name)
57
+ "Error: #{field_name} cannot be empty"
58
+ end
59
+ end
60
+ end