language-operator 0.1.65 → 0.1.66
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/Gemfile.lock +1 -1
- data/README.md +20 -1
- data/components/agent/Gemfile +1 -1
- data/docs/observability.md +208 -0
- data/lib/language_operator/agent/task_executor.rb +11 -1
- data/lib/language_operator/agent.rb +24 -14
- data/lib/language_operator/cli/commands/agent/base.rb +140 -47
- data/lib/language_operator/cli/commands/agent/code_operations.rb +157 -16
- data/lib/language_operator/cli/errors/suggestions.rb +1 -1
- data/lib/language_operator/constants.rb +1 -0
- data/lib/language_operator/kubernetes/client.rb +1 -1
- data/lib/language_operator/version.rb +1 -1
- data/synth/003/Makefile +12 -2
- data/synth/003/agent.txt +1 -1
- metadata +4 -6
- data/lib/language_operator/cli/commands/agent/learning.rb +0 -408
- data/synth/003/agent.optimized.rb +0 -66
- data/synth/003/agent.synthesized.rb +0 -41
|
@@ -4,42 +4,57 @@ module LanguageOperator
|
|
|
4
4
|
module CLI
|
|
5
5
|
module Commands
|
|
6
6
|
module Agent
|
|
7
|
-
# Code viewing and editing for agents
|
|
7
|
+
# Code viewing and editing for agents using LanguageAgentVersion CRD
|
|
8
8
|
module CodeOperations
|
|
9
9
|
def self.included(base)
|
|
10
10
|
base.class_eval do
|
|
11
|
-
desc 'code NAME', 'Display
|
|
11
|
+
desc 'code NAME', 'Display agent code from LanguageAgentVersion'
|
|
12
12
|
option :cluster, type: :string, desc: 'Override current cluster context'
|
|
13
13
|
option :raw, type: :boolean, default: false, desc: 'Output raw code without formatting'
|
|
14
|
+
option :version, type: :string, desc: 'Display specific version (e.g., --version=2)'
|
|
14
15
|
def code(name)
|
|
15
16
|
handle_command_error('get code') do
|
|
16
17
|
require_relative '../../formatters/code_formatter'
|
|
17
18
|
|
|
18
19
|
ctx = CLI::Helpers::ClusterContext.from_options(options)
|
|
19
20
|
|
|
20
|
-
# Get the
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Formatters::ProgressFormatter.error("Synthesized code not found for agent '#{name}'")
|
|
21
|
+
# Get the LanguageAgentVersion resource
|
|
22
|
+
version_resource = get_agent_version_resource(ctx, name, options[:version])
|
|
23
|
+
|
|
24
|
+
unless version_resource
|
|
25
|
+
Formatters::ProgressFormatter.error("No code versions found for agent '#{name}'")
|
|
26
26
|
puts
|
|
27
|
-
puts '
|
|
28
|
-
puts ' - Agent
|
|
27
|
+
puts 'This may indicate:'
|
|
28
|
+
puts ' - Agent has not been synthesized yet'
|
|
29
29
|
puts ' - Agent synthesis failed'
|
|
30
30
|
puts
|
|
31
|
-
puts 'Check
|
|
31
|
+
puts 'Check agent status with:'
|
|
32
32
|
puts " aictl agent inspect #{name}"
|
|
33
33
|
exit 1
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
# Get
|
|
37
|
-
code_content =
|
|
36
|
+
# Get code from LanguageAgentVersion spec
|
|
37
|
+
code_content = version_resource.dig('spec', 'code')
|
|
38
38
|
unless code_content
|
|
39
|
-
Formatters::ProgressFormatter.error('Code content not found in
|
|
39
|
+
Formatters::ProgressFormatter.error('Code content not found in LanguageAgentVersion')
|
|
40
40
|
exit 1
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
# Determine version info for title
|
|
44
|
+
version_num = version_resource.dig('spec', 'version')
|
|
45
|
+
source_type = version_resource.dig('spec', 'sourceType') || 'manual'
|
|
46
|
+
|
|
47
|
+
title = if options[:version]
|
|
48
|
+
"Code for Agent: #{name} (Version #{version_num})"
|
|
49
|
+
else
|
|
50
|
+
active_version = get_active_version(ctx, name)
|
|
51
|
+
if version_num.to_s == active_version
|
|
52
|
+
"Current Code for Agent: #{name} (Version #{version_num} - #{source_type})"
|
|
53
|
+
else
|
|
54
|
+
"Code for Agent: #{name} (Version #{version_num} - #{source_type})"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
43
58
|
# Raw output mode - just print the code
|
|
44
59
|
if options[:raw]
|
|
45
60
|
puts code_content
|
|
@@ -49,11 +64,137 @@ module LanguageOperator
|
|
|
49
64
|
# Display with syntax highlighting
|
|
50
65
|
Formatters::CodeFormatter.display_ruby_code(
|
|
51
66
|
code_content,
|
|
52
|
-
title:
|
|
67
|
+
title: title
|
|
53
68
|
)
|
|
54
69
|
end
|
|
55
70
|
end
|
|
56
71
|
|
|
72
|
+
desc 'versions NAME', 'List available LanguageAgentVersion resources'
|
|
73
|
+
option :cluster, type: :string, desc: 'Override current cluster context'
|
|
74
|
+
def versions(name)
|
|
75
|
+
handle_command_error('list versions') do
|
|
76
|
+
ctx = CLI::Helpers::ClusterContext.from_options(options)
|
|
77
|
+
|
|
78
|
+
# Get agent to verify it exists
|
|
79
|
+
get_resource_or_exit(Constants::RESOURCE_AGENT, name)
|
|
80
|
+
|
|
81
|
+
# Find all LanguageAgentVersion resources for this agent
|
|
82
|
+
versions = ctx.client.list_resources(Constants::RESOURCE_AGENT_VERSION, namespace: ctx.namespace)
|
|
83
|
+
.select { |v| v.dig('spec', 'agentRef', 'name') == name }
|
|
84
|
+
.sort_by { |v| v.dig('spec', 'version').to_i }
|
|
85
|
+
|
|
86
|
+
if versions.empty?
|
|
87
|
+
puts
|
|
88
|
+
puts "No LanguageAgentVersion resources found for agent: #{pastel.bold(name)}"
|
|
89
|
+
puts
|
|
90
|
+
puts pastel.yellow('No versions have been created yet.')
|
|
91
|
+
puts
|
|
92
|
+
puts 'Versions are created automatically during agent synthesis.'
|
|
93
|
+
puts "Check agent status: #{pastel.dim("aictl agent inspect #{name}")}"
|
|
94
|
+
return
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Get current active version
|
|
98
|
+
active_version = get_active_version(ctx, name)
|
|
99
|
+
|
|
100
|
+
# Build table data
|
|
101
|
+
table_data = versions.map do |version_resource|
|
|
102
|
+
version_num = version_resource.dig('spec', 'version').to_s
|
|
103
|
+
status = version_num == active_version ? 'current' : 'available'
|
|
104
|
+
source_type = version_resource.dig('spec', 'sourceType') || 'manual'
|
|
105
|
+
created = version_resource.dig('metadata', 'creationTimestamp')
|
|
106
|
+
created = created ? Time.parse(created).strftime('%Y-%m-%d %H:%M') : 'Unknown'
|
|
107
|
+
|
|
108
|
+
{
|
|
109
|
+
version: "v#{version_num}",
|
|
110
|
+
status: status,
|
|
111
|
+
type: source_type.capitalize,
|
|
112
|
+
created: created
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Display table
|
|
117
|
+
headers = %w[VERSION STATUS TYPE CREATED]
|
|
118
|
+
rows = table_data.map do |row|
|
|
119
|
+
status_indicator = row[:status] == 'current' ? pastel.green('●') : pastel.dim('○')
|
|
120
|
+
status_text = row[:status] == 'current' ? pastel.green('current') : 'available'
|
|
121
|
+
|
|
122
|
+
[
|
|
123
|
+
row[:version],
|
|
124
|
+
"#{status_indicator} #{status_text}",
|
|
125
|
+
row[:type],
|
|
126
|
+
row[:created]
|
|
127
|
+
]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
puts
|
|
131
|
+
puts "Code versions for agent: #{pastel.bold(name)}"
|
|
132
|
+
puts
|
|
133
|
+
puts table(headers, rows)
|
|
134
|
+
puts
|
|
135
|
+
puts 'Usage:'
|
|
136
|
+
puts " #{pastel.dim("aictl agent code #{name}")}"
|
|
137
|
+
puts " #{pastel.dim("aictl agent code #{name} --version=X")}"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
def get_active_version(ctx, agent_name)
|
|
144
|
+
begin
|
|
145
|
+
agent = ctx.client.get_resource(Constants::RESOURCE_AGENT, agent_name, ctx.namespace)
|
|
146
|
+
version_ref = agent.dig('spec', 'agentVersionRef', 'name')
|
|
147
|
+
return nil unless version_ref
|
|
148
|
+
|
|
149
|
+
# Extract version number from "agent-name-vX" format
|
|
150
|
+
version_ref.split('-v').last
|
|
151
|
+
rescue K8s::Error::NotFound
|
|
152
|
+
nil
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def get_agent_version_resource(ctx, agent_name, requested_version = nil)
|
|
157
|
+
# Query LanguageAgentVersion resources for this agent
|
|
158
|
+
versions = ctx.client.list_resources(Constants::RESOURCE_AGENT_VERSION, namespace: ctx.namespace)
|
|
159
|
+
.select { |v| v.dig('spec', 'agentRef', 'name') == agent_name }
|
|
160
|
+
|
|
161
|
+
if versions.empty?
|
|
162
|
+
return nil
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
if requested_version
|
|
166
|
+
# Find specific version
|
|
167
|
+
target_version = versions.find { |v| v.dig('spec', 'version').to_s == requested_version }
|
|
168
|
+
|
|
169
|
+
unless target_version
|
|
170
|
+
Formatters::ProgressFormatter.error("Version #{requested_version} not found for agent '#{agent_name}'")
|
|
171
|
+
puts
|
|
172
|
+
puts 'Available versions:'
|
|
173
|
+
versions.each do |v|
|
|
174
|
+
version_num = v.dig('spec', 'version')
|
|
175
|
+
puts " v#{version_num}"
|
|
176
|
+
end
|
|
177
|
+
puts
|
|
178
|
+
puts "Use 'aictl agent versions #{agent_name}' to see all available versions"
|
|
179
|
+
exit 1
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
return target_version
|
|
183
|
+
else
|
|
184
|
+
# Get currently active version
|
|
185
|
+
active_version = get_active_version(ctx, agent_name)
|
|
186
|
+
|
|
187
|
+
if active_version
|
|
188
|
+
# Find the currently active version resource
|
|
189
|
+
active_version_resource = versions.find { |v| v.dig('spec', 'version').to_s == active_version }
|
|
190
|
+
return active_version_resource if active_version_resource
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Fall back to latest version if no active version or active version not found
|
|
194
|
+
return versions.max_by { |v| v.dig('spec', 'version').to_i }
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
57
198
|
desc 'edit NAME', 'Edit agent instructions'
|
|
58
199
|
option :cluster, type: :string, desc: 'Override current cluster context'
|
|
59
200
|
def edit(name)
|
|
@@ -99,4 +240,4 @@ module LanguageOperator
|
|
|
99
240
|
end
|
|
100
241
|
end
|
|
101
242
|
end
|
|
102
|
-
end
|
|
243
|
+
end
|
|
@@ -51,7 +51,7 @@ module LanguageOperator
|
|
|
51
51
|
def agent_not_found_suggestions(context)
|
|
52
52
|
suggestions = []
|
|
53
53
|
suggestions << "List all agents: #{pastel.dim('aictl agent list')}"
|
|
54
|
-
suggestions << "Create a new agent: #{pastel.dim('aictl agent create
|
|
54
|
+
suggestions << "Create a new agent: #{pastel.dim('aictl agent create "description"')}"
|
|
55
55
|
suggestions << "Use the wizard: #{pastel.dim('aictl agent create --wizard')}" if context[:suggest_wizard]
|
|
56
56
|
suggestions
|
|
57
57
|
end
|
|
@@ -60,6 +60,7 @@ module LanguageOperator
|
|
|
60
60
|
# Kubernetes Custom Resource Definitions (CRD) kinds
|
|
61
61
|
# These replace magic strings scattered across CLI commands
|
|
62
62
|
RESOURCE_AGENT = 'LanguageAgent'
|
|
63
|
+
RESOURCE_AGENT_VERSION = 'LanguageAgentVersion'
|
|
63
64
|
RESOURCE_MODEL = 'LanguageModel'
|
|
64
65
|
RESOURCE_TOOL = 'LanguageTool'
|
|
65
66
|
RESOURCE_PERSONA = 'LanguagePersona'
|
|
@@ -385,7 +385,7 @@ module LanguageOperator
|
|
|
385
385
|
|
|
386
386
|
def default_api_version(kind)
|
|
387
387
|
case kind.downcase
|
|
388
|
-
when 'languagecluster', 'languageagent', 'languagetool', 'languagemodel', 'languageclient', 'languagepersona'
|
|
388
|
+
when 'languagecluster', 'languageagent', 'languageagentversion', 'languagetool', 'languagemodel', 'languageclient', 'languagepersona'
|
|
389
389
|
'langop.io/v1alpha1'
|
|
390
390
|
when 'namespace', 'configmap', 'secret', 'service'
|
|
391
391
|
'v1'
|
data/synth/003/Makefile
CHANGED
|
@@ -10,24 +10,34 @@ create:
|
|
|
10
10
|
run:
|
|
11
11
|
@JOB_NAME=$(AGENT)-$(shell date +%s); \
|
|
12
12
|
kubectl create job --from=cronjob/$(AGENT) $$JOB_NAME && \
|
|
13
|
-
trap "kubectl delete job $$JOB_NAME" EXIT; \
|
|
14
13
|
kubectl wait --for=condition=ready pod -l job-name=$$JOB_NAME --timeout=60s && \
|
|
15
|
-
kubectl logs -f job/$$JOB_NAME
|
|
14
|
+
kubectl logs -f job/$$JOB_NAME && \
|
|
15
|
+
sleep 15 && \
|
|
16
|
+
kubectl delete job $$JOB_NAME
|
|
16
17
|
|
|
17
18
|
code:
|
|
18
19
|
$(AICTL) code $(AGENT)
|
|
19
20
|
|
|
21
|
+
versions:
|
|
22
|
+
$(AICTL) versions $(AGENT)
|
|
23
|
+
|
|
20
24
|
inspect:
|
|
21
25
|
$(AICTL) inspect $(AGENT)
|
|
22
26
|
|
|
23
27
|
learning-status:
|
|
24
28
|
$(AICTL) learning status $(AGENT)
|
|
25
29
|
|
|
30
|
+
learn:
|
|
31
|
+
kubectl patch languageagent $(AGENT) --type='merge' -p='{"status":{"runsPendingLearning":10}}'
|
|
32
|
+
|
|
33
|
+
|
|
26
34
|
logs:
|
|
27
35
|
$(AICTL) logs $(AGENT)
|
|
28
36
|
|
|
29
37
|
clean:
|
|
30
38
|
$(AICTL) delete $(AGENT) --force
|
|
39
|
+
kubectl delete configmaps -l app.kubernetes.io/name=$(AGENT) --ignore-not-found=true
|
|
40
|
+
kubectl delete configmaps --field-selector metadata.name~=$(AGENT)-v --ignore-not-found=true
|
|
31
41
|
|
|
32
42
|
save:
|
|
33
43
|
$(AICTL) code $(AGENT) --raw > agent.rb
|
data/synth/003/agent.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Write a story one sentence at a time, with one new sentence every
|
|
1
|
+
Write a story one sentence at a time, with one new sentence every 10 minutes.
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: language-operator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.66
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Ryan
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2025-12-09 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: mcp
|
|
@@ -502,6 +502,7 @@ files:
|
|
|
502
502
|
- docs/constraints.md
|
|
503
503
|
- docs/how-agents-work.md
|
|
504
504
|
- docs/installation.md
|
|
505
|
+
- docs/observability.md
|
|
505
506
|
- docs/quickstart.md
|
|
506
507
|
- docs/schema-versioning.md
|
|
507
508
|
- docs/understanding-generated-code.md
|
|
@@ -533,7 +534,6 @@ files:
|
|
|
533
534
|
- lib/language_operator/cli/commands/agent/helpers/cluster_llm_client.rb
|
|
534
535
|
- lib/language_operator/cli/commands/agent/helpers/code_parser.rb
|
|
535
536
|
- lib/language_operator/cli/commands/agent/helpers/synthesis_watcher.rb
|
|
536
|
-
- lib/language_operator/cli/commands/agent/learning.rb
|
|
537
537
|
- lib/language_operator/cli/commands/agent/lifecycle.rb
|
|
538
538
|
- lib/language_operator/cli/commands/agent/logs.rb
|
|
539
539
|
- lib/language_operator/cli/commands/agent/workspace.rb
|
|
@@ -649,8 +649,6 @@ files:
|
|
|
649
649
|
- synth/002/output.log
|
|
650
650
|
- synth/003/Makefile
|
|
651
651
|
- synth/003/README.md
|
|
652
|
-
- synth/003/agent.optimized.rb
|
|
653
|
-
- synth/003/agent.synthesized.rb
|
|
654
652
|
- synth/003/agent.txt
|
|
655
653
|
- synth/004/Makefile
|
|
656
654
|
- synth/004/README.md
|
|
@@ -678,7 +676,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
678
676
|
- !ruby/object:Gem::Version
|
|
679
677
|
version: '0'
|
|
680
678
|
requirements: []
|
|
681
|
-
rubygems_version: 3.6.
|
|
679
|
+
rubygems_version: 3.6.6
|
|
682
680
|
specification_version: 4
|
|
683
681
|
summary: Ruby SDK for Language Operator
|
|
684
682
|
test_files: []
|