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.
- checksums.yaml +4 -4
- data/.claude/commands/persona.md +9 -0
- data/.claude/commands/task.md +46 -1
- data/.rubocop.yml +13 -0
- data/.rubocop_custom/use_ux_helper.rb +44 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +12 -1
- data/Makefile +26 -7
- data/Makefile.common +50 -0
- data/bin/aictl +8 -1
- data/components/agent/Gemfile +1 -1
- data/components/agent/bin/langop-agent +7 -0
- data/docs/README.md +58 -0
- data/docs/{dsl/best-practices.md → best-practices.md} +4 -4
- data/docs/cli-reference.md +274 -0
- data/docs/{dsl/constraints.md → constraints.md} +5 -5
- data/docs/how-agents-work.md +156 -0
- data/docs/installation.md +218 -0
- data/docs/quickstart.md +299 -0
- data/docs/understanding-generated-code.md +265 -0
- data/docs/using-tools.md +457 -0
- data/docs/webhooks.md +509 -0
- data/examples/ux_helpers_demo.rb +296 -0
- data/lib/language_operator/agent/base.rb +11 -1
- data/lib/language_operator/agent/executor.rb +23 -6
- data/lib/language_operator/agent/safety/safe_executor.rb +41 -39
- data/lib/language_operator/agent/task_executor.rb +346 -63
- data/lib/language_operator/agent/web_server.rb +110 -14
- data/lib/language_operator/agent/webhook_authenticator.rb +39 -5
- data/lib/language_operator/agent.rb +88 -2
- data/lib/language_operator/cli/base_command.rb +17 -11
- data/lib/language_operator/cli/command_loader.rb +72 -0
- data/lib/language_operator/cli/commands/agent/base.rb +837 -0
- data/lib/language_operator/cli/commands/agent/code_operations.rb +102 -0
- data/lib/language_operator/cli/commands/agent/helpers/cluster_llm_client.rb +116 -0
- data/lib/language_operator/cli/commands/agent/helpers/code_parser.rb +115 -0
- data/lib/language_operator/cli/commands/agent/helpers/synthesis_watcher.rb +96 -0
- data/lib/language_operator/cli/commands/agent/learning.rb +289 -0
- data/lib/language_operator/cli/commands/agent/lifecycle.rb +102 -0
- data/lib/language_operator/cli/commands/agent/logs.rb +125 -0
- data/lib/language_operator/cli/commands/agent/workspace.rb +327 -0
- data/lib/language_operator/cli/commands/cluster.rb +129 -84
- data/lib/language_operator/cli/commands/install.rb +1 -1
- data/lib/language_operator/cli/commands/model/base.rb +215 -0
- data/lib/language_operator/cli/commands/model/test.rb +165 -0
- data/lib/language_operator/cli/commands/persona.rb +16 -34
- data/lib/language_operator/cli/commands/quickstart.rb +3 -2
- data/lib/language_operator/cli/commands/status.rb +40 -67
- data/lib/language_operator/cli/commands/system/base.rb +44 -0
- data/lib/language_operator/cli/commands/system/exec.rb +147 -0
- data/lib/language_operator/cli/commands/system/helpers/llm_synthesis.rb +183 -0
- data/lib/language_operator/cli/commands/system/helpers/pod_manager.rb +212 -0
- data/lib/language_operator/cli/commands/system/helpers/template_loader.rb +57 -0
- data/lib/language_operator/cli/commands/system/helpers/template_validator.rb +174 -0
- data/lib/language_operator/cli/commands/system/schema.rb +92 -0
- data/lib/language_operator/cli/commands/system/synthesis_template.rb +151 -0
- data/lib/language_operator/cli/commands/system/synthesize.rb +224 -0
- data/lib/language_operator/cli/commands/system/validate_template.rb +130 -0
- data/lib/language_operator/cli/commands/tool/base.rb +271 -0
- data/lib/language_operator/cli/commands/tool/install.rb +255 -0
- data/lib/language_operator/cli/commands/tool/search.rb +69 -0
- data/lib/language_operator/cli/commands/tool/test.rb +115 -0
- data/lib/language_operator/cli/commands/use.rb +29 -6
- data/lib/language_operator/cli/errors/handler.rb +20 -17
- data/lib/language_operator/cli/errors/suggestions.rb +3 -5
- data/lib/language_operator/cli/errors/thor_errors.rb +55 -0
- data/lib/language_operator/cli/formatters/code_formatter.rb +4 -11
- data/lib/language_operator/cli/formatters/log_formatter.rb +8 -15
- data/lib/language_operator/cli/formatters/progress_formatter.rb +6 -8
- data/lib/language_operator/cli/formatters/status_formatter.rb +26 -7
- data/lib/language_operator/cli/formatters/table_formatter.rb +47 -36
- data/lib/language_operator/cli/formatters/value_formatter.rb +75 -0
- data/lib/language_operator/cli/helpers/cluster_context.rb +5 -3
- data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +2 -1
- data/lib/language_operator/cli/helpers/label_utils.rb +97 -0
- data/lib/language_operator/{ux/concerns/provider_helpers.rb → cli/helpers/provider_helper.rb} +10 -29
- data/lib/language_operator/cli/helpers/schedule_builder.rb +21 -1
- data/lib/language_operator/cli/helpers/user_prompts.rb +19 -11
- data/lib/language_operator/cli/helpers/ux_helper.rb +538 -0
- data/lib/language_operator/{ux/concerns/input_validation.rb → cli/helpers/validation_helper.rb} +13 -66
- data/lib/language_operator/cli/main.rb +50 -40
- data/lib/language_operator/cli/templates/tools/generic.yaml +3 -0
- data/lib/language_operator/cli/wizards/agent_wizard.rb +12 -20
- data/lib/language_operator/cli/wizards/model_wizard.rb +271 -0
- data/lib/language_operator/cli/wizards/quickstart_wizard.rb +8 -34
- data/lib/language_operator/client/base.rb +28 -0
- data/lib/language_operator/client/config.rb +4 -1
- data/lib/language_operator/client/mcp_connector.rb +1 -1
- data/lib/language_operator/config/cluster_config.rb +3 -2
- data/lib/language_operator/config.rb +38 -11
- data/lib/language_operator/constants/kubernetes_labels.rb +80 -0
- data/lib/language_operator/constants.rb +13 -0
- data/lib/language_operator/dsl/http.rb +127 -10
- data/lib/language_operator/dsl.rb +153 -6
- data/lib/language_operator/errors.rb +50 -0
- data/lib/language_operator/kubernetes/client.rb +11 -6
- data/lib/language_operator/kubernetes/resource_builder.rb +58 -84
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +1 -1
- data/lib/language_operator/type_coercion.rb +118 -34
- data/lib/language_operator/utils/secure_path.rb +74 -0
- data/lib/language_operator/utils.rb +7 -0
- data/lib/language_operator/validators.rb +54 -2
- data/lib/language_operator/version.rb +1 -1
- data/synth/001/Makefile +10 -2
- data/synth/001/agent.rb +16 -15
- data/synth/001/output.log +27 -10
- data/synth/002/Makefile +10 -2
- data/synth/003/Makefile +1 -1
- data/synth/003/README.md +205 -133
- data/synth/003/agent.optimized.rb +66 -0
- data/synth/003/agent.synthesized.rb +41 -0
- metadata +111 -35
- data/docs/dsl/agent-reference.md +0 -604
- data/docs/dsl/mcp-integration.md +0 -1177
- data/docs/dsl/webhooks.md +0 -932
- data/docs/dsl/workflows.md +0 -744
- data/lib/language_operator/cli/commands/agent.rb +0 -1712
- data/lib/language_operator/cli/commands/model.rb +0 -366
- data/lib/language_operator/cli/commands/system.rb +0 -1259
- data/lib/language_operator/cli/commands/tool.rb +0 -654
- data/lib/language_operator/cli/formatters/optimization_formatter.rb +0 -226
- data/lib/language_operator/cli/helpers/pastel_helper.rb +0 -24
- data/lib/language_operator/learning/adapters/base_adapter.rb +0 -149
- data/lib/language_operator/learning/adapters/jaeger_adapter.rb +0 -221
- data/lib/language_operator/learning/adapters/signoz_adapter.rb +0 -435
- data/lib/language_operator/learning/adapters/tempo_adapter.rb +0 -239
- data/lib/language_operator/learning/optimizer.rb +0 -319
- data/lib/language_operator/learning/pattern_detector.rb +0 -260
- data/lib/language_operator/learning/task_synthesizer.rb +0 -288
- data/lib/language_operator/learning/trace_analyzer.rb +0 -285
- data/lib/language_operator/templates/task_synthesis.tmpl +0 -98
- data/lib/language_operator/ux/base.rb +0 -81
- data/lib/language_operator/ux/concerns/README.md +0 -155
- data/lib/language_operator/ux/concerns/headings.rb +0 -90
- data/lib/language_operator/ux/create_agent.rb +0 -255
- data/lib/language_operator/ux/create_model.rb +0 -267
- data/lib/language_operator/ux/quickstart.rb +0 -594
- data/synth/003/agent.rb +0 -41
- data/synth/003/output.log +0 -68
- /data/docs/{architecture/agent-runtime.md → agent-internals.md} +0 -0
- /data/docs/{dsl/chat-endpoints.md → chat-endpoints.md} +0 -0
- /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
|