language-operator 0.1.61 → 0.1.62

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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/persona.md +9 -0
  3. data/.claude/commands/task.md +46 -1
  4. data/.rubocop.yml +13 -0
  5. data/.rubocop_custom/use_ux_helper.rb +44 -0
  6. data/CHANGELOG.md +8 -0
  7. data/Gemfile.lock +12 -1
  8. data/Makefile +26 -7
  9. data/Makefile.common +50 -0
  10. data/bin/aictl +8 -1
  11. data/components/agent/Gemfile +1 -1
  12. data/components/agent/bin/langop-agent +7 -0
  13. data/docs/README.md +58 -0
  14. data/docs/{dsl/best-practices.md → best-practices.md} +4 -4
  15. data/docs/cli-reference.md +274 -0
  16. data/docs/{dsl/constraints.md → constraints.md} +5 -5
  17. data/docs/how-agents-work.md +156 -0
  18. data/docs/installation.md +218 -0
  19. data/docs/quickstart.md +299 -0
  20. data/docs/understanding-generated-code.md +265 -0
  21. data/docs/using-tools.md +457 -0
  22. data/docs/webhooks.md +509 -0
  23. data/examples/ux_helpers_demo.rb +296 -0
  24. data/lib/language_operator/agent/base.rb +11 -1
  25. data/lib/language_operator/agent/executor.rb +23 -6
  26. data/lib/language_operator/agent/safety/safe_executor.rb +41 -39
  27. data/lib/language_operator/agent/task_executor.rb +346 -63
  28. data/lib/language_operator/agent/web_server.rb +110 -14
  29. data/lib/language_operator/agent/webhook_authenticator.rb +39 -5
  30. data/lib/language_operator/agent.rb +88 -2
  31. data/lib/language_operator/cli/base_command.rb +17 -11
  32. data/lib/language_operator/cli/command_loader.rb +72 -0
  33. data/lib/language_operator/cli/commands/agent/base.rb +837 -0
  34. data/lib/language_operator/cli/commands/agent/code_operations.rb +102 -0
  35. data/lib/language_operator/cli/commands/agent/helpers/cluster_llm_client.rb +116 -0
  36. data/lib/language_operator/cli/commands/agent/helpers/code_parser.rb +115 -0
  37. data/lib/language_operator/cli/commands/agent/helpers/synthesis_watcher.rb +96 -0
  38. data/lib/language_operator/cli/commands/agent/learning.rb +289 -0
  39. data/lib/language_operator/cli/commands/agent/lifecycle.rb +102 -0
  40. data/lib/language_operator/cli/commands/agent/logs.rb +125 -0
  41. data/lib/language_operator/cli/commands/agent/workspace.rb +327 -0
  42. data/lib/language_operator/cli/commands/cluster.rb +129 -84
  43. data/lib/language_operator/cli/commands/install.rb +1 -1
  44. data/lib/language_operator/cli/commands/model/base.rb +215 -0
  45. data/lib/language_operator/cli/commands/model/test.rb +165 -0
  46. data/lib/language_operator/cli/commands/persona.rb +16 -34
  47. data/lib/language_operator/cli/commands/quickstart.rb +3 -2
  48. data/lib/language_operator/cli/commands/status.rb +40 -67
  49. data/lib/language_operator/cli/commands/system/base.rb +44 -0
  50. data/lib/language_operator/cli/commands/system/exec.rb +147 -0
  51. data/lib/language_operator/cli/commands/system/helpers/llm_synthesis.rb +183 -0
  52. data/lib/language_operator/cli/commands/system/helpers/pod_manager.rb +212 -0
  53. data/lib/language_operator/cli/commands/system/helpers/template_loader.rb +57 -0
  54. data/lib/language_operator/cli/commands/system/helpers/template_validator.rb +174 -0
  55. data/lib/language_operator/cli/commands/system/schema.rb +92 -0
  56. data/lib/language_operator/cli/commands/system/synthesis_template.rb +151 -0
  57. data/lib/language_operator/cli/commands/system/synthesize.rb +224 -0
  58. data/lib/language_operator/cli/commands/system/validate_template.rb +130 -0
  59. data/lib/language_operator/cli/commands/tool/base.rb +271 -0
  60. data/lib/language_operator/cli/commands/tool/install.rb +255 -0
  61. data/lib/language_operator/cli/commands/tool/search.rb +69 -0
  62. data/lib/language_operator/cli/commands/tool/test.rb +115 -0
  63. data/lib/language_operator/cli/commands/use.rb +29 -6
  64. data/lib/language_operator/cli/errors/handler.rb +20 -17
  65. data/lib/language_operator/cli/errors/suggestions.rb +3 -5
  66. data/lib/language_operator/cli/errors/thor_errors.rb +55 -0
  67. data/lib/language_operator/cli/formatters/code_formatter.rb +4 -11
  68. data/lib/language_operator/cli/formatters/log_formatter.rb +8 -15
  69. data/lib/language_operator/cli/formatters/progress_formatter.rb +6 -8
  70. data/lib/language_operator/cli/formatters/status_formatter.rb +26 -7
  71. data/lib/language_operator/cli/formatters/table_formatter.rb +47 -36
  72. data/lib/language_operator/cli/formatters/value_formatter.rb +75 -0
  73. data/lib/language_operator/cli/helpers/cluster_context.rb +5 -3
  74. data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +2 -1
  75. data/lib/language_operator/cli/helpers/label_utils.rb +97 -0
  76. data/lib/language_operator/{ux/concerns/provider_helpers.rb → cli/helpers/provider_helper.rb} +10 -29
  77. data/lib/language_operator/cli/helpers/schedule_builder.rb +21 -1
  78. data/lib/language_operator/cli/helpers/user_prompts.rb +19 -11
  79. data/lib/language_operator/cli/helpers/ux_helper.rb +538 -0
  80. data/lib/language_operator/{ux/concerns/input_validation.rb → cli/helpers/validation_helper.rb} +13 -66
  81. data/lib/language_operator/cli/main.rb +50 -40
  82. data/lib/language_operator/cli/templates/tools/generic.yaml +3 -0
  83. data/lib/language_operator/cli/wizards/agent_wizard.rb +12 -20
  84. data/lib/language_operator/cli/wizards/model_wizard.rb +271 -0
  85. data/lib/language_operator/cli/wizards/quickstart_wizard.rb +8 -34
  86. data/lib/language_operator/client/base.rb +28 -0
  87. data/lib/language_operator/client/config.rb +4 -1
  88. data/lib/language_operator/client/mcp_connector.rb +1 -1
  89. data/lib/language_operator/config/cluster_config.rb +3 -2
  90. data/lib/language_operator/config.rb +38 -11
  91. data/lib/language_operator/constants/kubernetes_labels.rb +80 -0
  92. data/lib/language_operator/constants.rb +13 -0
  93. data/lib/language_operator/dsl/http.rb +127 -10
  94. data/lib/language_operator/dsl.rb +153 -6
  95. data/lib/language_operator/errors.rb +50 -0
  96. data/lib/language_operator/kubernetes/client.rb +11 -6
  97. data/lib/language_operator/kubernetes/resource_builder.rb +58 -84
  98. data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
  99. data/lib/language_operator/templates/schema/agent_dsl_schema.json +1 -1
  100. data/lib/language_operator/type_coercion.rb +118 -34
  101. data/lib/language_operator/utils/secure_path.rb +74 -0
  102. data/lib/language_operator/utils.rb +7 -0
  103. data/lib/language_operator/validators.rb +54 -2
  104. data/lib/language_operator/version.rb +1 -1
  105. data/synth/001/Makefile +10 -2
  106. data/synth/001/agent.rb +16 -15
  107. data/synth/001/output.log +27 -10
  108. data/synth/002/Makefile +10 -2
  109. data/synth/003/Makefile +1 -1
  110. data/synth/003/README.md +205 -133
  111. data/synth/003/agent.optimized.rb +66 -0
  112. data/synth/003/agent.synthesized.rb +41 -0
  113. metadata +111 -35
  114. data/docs/dsl/agent-reference.md +0 -604
  115. data/docs/dsl/mcp-integration.md +0 -1177
  116. data/docs/dsl/webhooks.md +0 -932
  117. data/docs/dsl/workflows.md +0 -744
  118. data/lib/language_operator/cli/commands/agent.rb +0 -1712
  119. data/lib/language_operator/cli/commands/model.rb +0 -366
  120. data/lib/language_operator/cli/commands/system.rb +0 -1259
  121. data/lib/language_operator/cli/commands/tool.rb +0 -654
  122. data/lib/language_operator/cli/formatters/optimization_formatter.rb +0 -226
  123. data/lib/language_operator/cli/helpers/pastel_helper.rb +0 -24
  124. data/lib/language_operator/learning/adapters/base_adapter.rb +0 -149
  125. data/lib/language_operator/learning/adapters/jaeger_adapter.rb +0 -221
  126. data/lib/language_operator/learning/adapters/signoz_adapter.rb +0 -435
  127. data/lib/language_operator/learning/adapters/tempo_adapter.rb +0 -239
  128. data/lib/language_operator/learning/optimizer.rb +0 -319
  129. data/lib/language_operator/learning/pattern_detector.rb +0 -260
  130. data/lib/language_operator/learning/task_synthesizer.rb +0 -288
  131. data/lib/language_operator/learning/trace_analyzer.rb +0 -285
  132. data/lib/language_operator/templates/task_synthesis.tmpl +0 -98
  133. data/lib/language_operator/ux/base.rb +0 -81
  134. data/lib/language_operator/ux/concerns/README.md +0 -155
  135. data/lib/language_operator/ux/concerns/headings.rb +0 -90
  136. data/lib/language_operator/ux/create_agent.rb +0 -255
  137. data/lib/language_operator/ux/create_model.rb +0 -267
  138. data/lib/language_operator/ux/quickstart.rb +0 -594
  139. data/synth/003/agent.rb +0 -41
  140. data/synth/003/output.log +0 -68
  141. /data/docs/{architecture/agent-runtime.md → agent-internals.md} +0 -0
  142. /data/docs/{dsl/chat-endpoints.md → chat-endpoints.md} +0 -0
  143. /data/docs/{dsl/SCHEMA_VERSION.md → schema-versioning.md} +0 -0
@@ -1,366 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'json'
5
- require 'English'
6
- require 'shellwords'
7
- require_relative '../base_command'
8
- require_relative '../formatters/progress_formatter'
9
- require_relative '../formatters/table_formatter'
10
- require_relative '../helpers/cluster_validator'
11
- require_relative '../helpers/user_prompts'
12
- require_relative '../helpers/resource_dependency_checker'
13
- require_relative '../helpers/editor_helper'
14
- require_relative '../../config/cluster_config'
15
- require_relative '../../kubernetes/client'
16
- require_relative '../../kubernetes/resource_builder'
17
- require_relative '../../ux/create_model'
18
-
19
- module LanguageOperator
20
- module CLI
21
- module Commands
22
- # Model management commands
23
- class Model < BaseCommand
24
- include Helpers::ClusterValidator
25
-
26
- desc 'list', 'List all models in current cluster'
27
- option :cluster, type: :string, desc: 'Override current cluster context'
28
- def list
29
- handle_command_error('list models') do
30
- models = list_resources_or_empty('LanguageModel') do
31
- puts
32
- puts 'Models define LLM configurations for agents.'
33
- puts
34
- puts 'Create a model with:'
35
- puts ' aictl model create <name> --provider <provider> --model <model>'
36
- end
37
-
38
- return if models.empty?
39
-
40
- table_data = models.map do |model|
41
- name = model.dig('metadata', 'name')
42
- provider = model.dig('spec', 'provider') || 'unknown'
43
- model_name = model.dig('spec', 'modelName') || 'unknown'
44
- status = model.dig('status', 'phase') || 'Unknown'
45
-
46
- {
47
- name: name,
48
- provider: provider,
49
- model: model_name,
50
- status: status
51
- }
52
- end
53
-
54
- Formatters::TableFormatter.models(table_data)
55
- end
56
- end
57
-
58
- desc 'create [NAME]', 'Create a new model'
59
- long_desc <<-DESC
60
- Create a new LanguageModel resource in the cluster.
61
-
62
- If NAME is omitted and no options are provided, an interactive wizard will guide you.
63
-
64
- Examples:
65
- aictl model create # Launch interactive wizard
66
- aictl model create gpt4 --provider openai --model gpt-4-turbo
67
- aictl model create claude --provider anthropic --model claude-3-opus-20240229
68
- aictl model create local --provider openai_compatible --model llama-3 --endpoint http://localhost:8080
69
- DESC
70
- option :provider, type: :string, required: false, desc: 'LLM provider (e.g., openai, anthropic, openai_compatible)'
71
- option :model, type: :string, required: false, desc: 'Model identifier (e.g., gpt-4, claude-3-opus)'
72
- option :endpoint, type: :string, desc: 'Custom endpoint URL (for openai_compatible or self-hosted)'
73
- option :cluster, type: :string, desc: 'Override current cluster context'
74
- option :dry_run, type: :boolean, default: false, desc: 'Output the manifest without creating'
75
- def create(name = nil)
76
- handle_command_error('create model') do
77
- # Launch interactive wizard if no arguments provided
78
- if name.nil? && options[:provider].nil? && options[:model].nil?
79
- Ux::CreateModel.execute(ctx)
80
- return
81
- end
82
-
83
- # Validate required options for non-interactive mode
84
- if options[:provider].nil? || options[:model].nil?
85
- Formatters::ProgressFormatter.error(
86
- 'Must provide both --provider and --model, or use interactive mode (run without arguments)'
87
- )
88
- exit 1
89
- end
90
-
91
- # Build LanguageModel resource
92
- resource = Kubernetes::ResourceBuilder.language_model(
93
- name,
94
- provider: options[:provider],
95
- model: options[:model],
96
- endpoint: options[:endpoint],
97
- cluster: ctx.namespace
98
- )
99
-
100
- # Handle dry-run: output manifest and exit
101
- if options[:dry_run]
102
- puts resource.to_yaml
103
- return
104
- end
105
-
106
- # Check if model already exists
107
- begin
108
- ctx.client.get_resource('LanguageModel', name, ctx.namespace)
109
- Formatters::ProgressFormatter.error("Model '#{name}' already exists in cluster '#{ctx.name}'")
110
- exit 1
111
- rescue K8s::Error::NotFound
112
- # Model doesn't exist, proceed with creation
113
- end
114
-
115
- # Create model
116
- Formatters::ProgressFormatter.with_spinner("Creating model '#{name}'") do
117
- ctx.client.apply_resource(resource)
118
- end
119
-
120
- Formatters::ProgressFormatter.success("Model '#{name}' created successfully")
121
- puts
122
- puts 'Model Details:'
123
- puts " Name: #{name}"
124
- puts " Provider: #{options[:provider]}"
125
- puts " Model: #{options[:model]}"
126
- puts " Endpoint: #{options[:endpoint]}" if options[:endpoint]
127
- puts " Cluster: #{ctx.name}"
128
- end
129
- end
130
-
131
- desc 'inspect NAME', 'Show detailed model information'
132
- option :cluster, type: :string, desc: 'Override current cluster context'
133
- def inspect(name)
134
- handle_command_error('inspect model') do
135
- model = get_resource_or_exit('LanguageModel', name)
136
-
137
- puts "Model: #{name}"
138
- puts " Cluster: #{ctx.name}"
139
- puts " Namespace: #{ctx.namespace}"
140
- puts " Provider: #{model.dig('spec', 'provider')}"
141
- puts " Model: #{model.dig('spec', 'modelName')}"
142
- puts " Endpoint: #{model.dig('spec', 'endpoint')}" if model.dig('spec', 'endpoint')
143
- puts " Status: #{model.dig('status', 'phase') || 'Unknown'}"
144
- puts
145
-
146
- # Get agents using this model
147
- agents = ctx.client.list_resources('LanguageAgent', namespace: ctx.namespace)
148
- agents_using = Helpers::ResourceDependencyChecker.agents_using_model(agents, name)
149
-
150
- if agents_using.any?
151
- puts "Agents using this model (#{agents_using.count}):"
152
- agents_using.each do |agent|
153
- puts " - #{agent.dig('metadata', 'name')}"
154
- end
155
- else
156
- puts 'No agents using this model'
157
- end
158
-
159
- puts
160
- puts 'Labels:'
161
- labels = model.dig('metadata', 'labels') || {}
162
- if labels.empty?
163
- puts ' (none)'
164
- else
165
- labels.each do |key, value|
166
- puts " #{key}: #{value}"
167
- end
168
- end
169
- end
170
- end
171
-
172
- desc 'delete NAME', 'Delete a model'
173
- option :cluster, type: :string, desc: 'Override current cluster context'
174
- option :force, type: :boolean, default: false, desc: 'Skip confirmation'
175
- def delete(name)
176
- handle_command_error('delete model') do
177
- model = get_resource_or_exit('LanguageModel', name)
178
-
179
- # Check dependencies and get confirmation
180
- return unless check_dependencies_and_confirm('model', name, force: options[:force])
181
-
182
- # Confirm deletion unless --force
183
- if confirm_deletion(
184
- 'model', name, ctx.name,
185
- details: {
186
- 'Provider' => model.dig('spec', 'provider'),
187
- 'Model' => model.dig('spec', 'modelName'),
188
- 'Status' => model.dig('status', 'phase')
189
- },
190
- force: options[:force]
191
- )
192
- # Delete model
193
- Formatters::ProgressFormatter.with_spinner("Deleting model '#{name}'") do
194
- ctx.client.delete_resource('LanguageModel', name, ctx.namespace)
195
- end
196
-
197
- Formatters::ProgressFormatter.success("Model '#{name}' deleted successfully")
198
- end
199
- end
200
- end
201
-
202
- desc 'edit NAME', 'Edit model configuration'
203
- option :cluster, type: :string, desc: 'Override current cluster context'
204
- def edit(name)
205
- handle_command_error('edit model') do
206
- model = get_resource_or_exit('LanguageModel', name)
207
-
208
- # Edit model YAML in user's editor
209
- edited_yaml = Helpers::EditorHelper.edit_content(
210
- model.to_yaml,
211
- 'model-',
212
- '.yaml',
213
- default_editor: 'vim'
214
- )
215
- edited_model = YAML.safe_load(edited_yaml)
216
-
217
- # Apply changes
218
- Formatters::ProgressFormatter.with_spinner("Updating model '#{name}'") do
219
- ctx.client.apply_resource(edited_model)
220
- end
221
-
222
- Formatters::ProgressFormatter.success("Model '#{name}' updated successfully")
223
- end
224
- end
225
-
226
- desc 'test NAME', 'Test model connectivity and functionality'
227
- long_desc <<-DESC
228
- Test that a model is operational by:
229
- 1. Verifying the pod is running
230
- 2. Testing the chat completion endpoint with a simple message
231
-
232
- This command helps diagnose model deployment issues.
233
- DESC
234
- option :cluster, type: :string, desc: 'Override current cluster context'
235
- option :timeout, type: :numeric, default: 30, desc: 'Timeout in seconds for endpoint test'
236
- def test(name)
237
- handle_command_error('test model') do
238
- # 1. Get model resource
239
- model = get_resource_or_exit('LanguageModel', name)
240
- model_name = model.dig('spec', 'modelName')
241
- model.dig('spec', 'provider')
242
-
243
- # 2. Check deployment status
244
- deployment = check_deployment_status(name)
245
-
246
- # 3. Check pod status
247
- pod = check_pod_status(name, deployment)
248
-
249
- # 4. Test chat completion endpoint
250
- test_chat_completion(name, model_name, pod, options[:timeout])
251
- end
252
- end
253
-
254
- private
255
-
256
- def check_deployment_status(name)
257
- Formatters::ProgressFormatter.with_spinner('Verifying deployment') do
258
- deployment = ctx.client.get_resource('Deployment', name, ctx.namespace)
259
- replicas = deployment.dig('spec', 'replicas') || 1
260
- ready_replicas = deployment.dig('status', 'readyReplicas') || 0
261
-
262
- unless ready_replicas >= replicas
263
- raise "Deployment not ready (#{ready_replicas}/#{replicas}). " \
264
- "Run 'kubectl get deployment #{name} -n #{ctx.namespace}' for details."
265
- end
266
-
267
- deployment
268
- end
269
- rescue K8s::Error::NotFound
270
- Formatters::ProgressFormatter.error("Deployment '#{name}' not found")
271
- exit 1
272
- end
273
-
274
- def check_pod_status(name, deployment)
275
- Formatters::ProgressFormatter.with_spinner('Verifying pod') do
276
- labels = deployment.dig('spec', 'selector', 'matchLabels')
277
- raise "Deployment '#{name}' has no selector labels" if labels.nil?
278
-
279
- # Convert K8s::Resource to hash if needed
280
- labels_hash = labels.respond_to?(:to_h) ? labels.to_h : labels
281
- raise "Deployment '#{name}' has empty selector labels" if labels_hash.empty?
282
-
283
- label_selector = labels_hash.map { |k, v| "#{k}=#{v}" }.join(',')
284
-
285
- pods = ctx.client.list_resources('Pod', namespace: ctx.namespace, label_selector: label_selector)
286
- raise "No pods found for model '#{name}'" if pods.empty?
287
-
288
- # Find a running pod
289
- running_pod = pods.find do |pod|
290
- pod.dig('status', 'phase') == 'Running' &&
291
- pod.dig('status', 'conditions')&.any? { |c| c['type'] == 'Ready' && c['status'] == 'True' }
292
- end
293
-
294
- if running_pod.nil?
295
- pod_phases = pods.map { |p| p.dig('status', 'phase') }.join(', ')
296
- raise "No running pods found. Pod phases: #{pod_phases}. " \
297
- "Run 'kubectl get pods -l #{label_selector} -n #{ctx.namespace}' for details."
298
- end
299
-
300
- running_pod
301
- end
302
- end
303
-
304
- def test_chat_completion(_name, model_name, pod, timeout)
305
- Formatters::ProgressFormatter.with_spinner('Verifying chat completion requests') do
306
- pod_name = pod.dig('metadata', 'name')
307
-
308
- # Build the JSON payload
309
- payload = JSON.generate({
310
- model: model_name,
311
- messages: [{ role: 'user', content: 'hello' }],
312
- max_tokens: 10
313
- })
314
-
315
- # Build the curl command using echo to pipe JSON
316
- # This avoids shell escaping issues with -d flag
317
- curl_command = "echo '#{payload}' | curl -s -X POST http://localhost:4000/v1/chat/completions " \
318
- "-H 'Content-Type: application/json' -d @- --max-time #{timeout}"
319
-
320
- # Execute the curl command inside the pod
321
- result = execute_in_pod(pod_name, curl_command)
322
-
323
- # Parse the response
324
- response = JSON.parse(result)
325
-
326
- if response['error']
327
- error_msg = response['error']['message'] || response['error']
328
- raise error_msg
329
- elsif !response['choices']
330
- raise "Unexpected response format: #{result.lines.first.strip}"
331
- end
332
-
333
- response
334
- rescue JSON::ParserError => e
335
- raise "Failed to parse response: #{e.message}"
336
- end
337
- rescue StandardError => e
338
- # Display error in bold red
339
- puts
340
- puts Formatters::ProgressFormatter.pastel.bold.red(e.message)
341
- exit 1
342
- end
343
-
344
- def execute_in_pod(pod_name, command)
345
- # Use kubectl exec to run command in pod
346
- # command can be a string or array
347
- kubectl_command = if command.is_a?(String)
348
- "kubectl exec -n #{ctx.namespace} #{pod_name} -- sh -c #{Shellwords.escape(command)}"
349
- else
350
- (['kubectl', 'exec', '-n', ctx.namespace, pod_name, '--'] + command).join(' ')
351
- end
352
-
353
- output = `#{kubectl_command} 2>&1`
354
- exit_code = $CHILD_STATUS.exitstatus
355
-
356
- if exit_code != 0
357
- Formatters::ProgressFormatter.error("Failed to execute command in pod: #{output}")
358
- exit 1
359
- end
360
-
361
- output
362
- end
363
- end
364
- end
365
- end
366
- end