language-operator 0.1.52 → 0.1.53

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11e9d5df0ee7675d0e0f2bccbc2fcb9eaee376e5ad9f7d11a8d8f0000c994bb2
4
- data.tar.gz: 0d8ac88a87ac9037934084d3e616ab1107e4e4fa26ae138809e7c64ca6924ca1
3
+ metadata.gz: 362d72d195bf272f98204cba33e7f8bf6513c7544a6bf6d6369c2ef1b9cfe793
4
+ data.tar.gz: d75edb537a7bec08fbba3a6b96dc05e9ecdbf62d7e80b9d33176f365623f4542
5
5
  SHA512:
6
- metadata.gz: 452afc411ecdf258e31636750e63305b682a89dd07039071e5b43d68b2de2d37ecde1a5d2b362a2a96d28bc3f934dfd5fd90df54c483e0321ba46f5bff3aa037
7
- data.tar.gz: 0fd49b16dc6b2db57ee1be9a165bfbe9e5eecccb6051cde1ed8d417caf2ca0949160ad357f516603a9f8afdfc300e8432fd5e9c96b88bf3f4fcca8fd149d00b0
6
+ metadata.gz: 7c3c378dfb4a013a1590b7c3295ad7f948a74d076f9ae38e50d587c7dfc26f254669c2586ff02aaa6fd8f1c5b933d67f627d7777f950f5bcacf59575b56c6da1
7
+ data.tar.gz: 7f956e31db63897853f31d237181c54f85b6c352d350ce4d3347bffaec0d4aaca7d2a2082b9c0092bd0f7972938271d9309e85ecb835679fca9e19008ec6dc2f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- language-operator (0.1.52)
4
+ language-operator (0.1.53)
5
5
  faraday (~> 2.0)
6
6
  k8s-ruby (~> 0.17)
7
7
  mcp (~> 0.4)
@@ -17,7 +17,6 @@ PATH
17
17
  rouge (~> 4.0)
18
18
  ruby_llm (~> 1.8)
19
19
  ruby_llm-mcp (~> 0.1)
20
- rufus-scheduler (~> 3.9)
21
20
  thor (~> 1.3)
22
21
  tty-prompt (~> 0.23)
23
22
  tty-spinner (~> 0.9)
@@ -62,8 +61,6 @@ GEM
62
61
  dry-inflector (~> 1.0)
63
62
  dry-logic (~> 1.4)
64
63
  zeitwerk (~> 2.6)
65
- et-orbi (1.4.0)
66
- tzinfo
67
64
  event_stream_parser (1.0.0)
68
65
  excon (0.112.0)
69
66
  faraday (2.14.0)
@@ -76,9 +73,6 @@ GEM
76
73
  net-http (>= 0.5.0)
77
74
  faraday-retry (2.3.2)
78
75
  faraday (~> 2.0)
79
- fugit (1.12.1)
80
- et-orbi (~> 1.4)
81
- raabro (~> 1.4)
82
76
  google-protobuf (4.33.0)
83
77
  bigdecimal
84
78
  rake (>= 13)
@@ -164,7 +158,6 @@ GEM
164
158
  public_suffix (6.0.2)
165
159
  puma (6.6.1)
166
160
  nio4r (~> 2.0)
167
- raabro (1.4.0)
168
161
  racc (1.8.1)
169
162
  rack (3.2.4)
170
163
  rack-test (2.2.0)
@@ -226,8 +219,6 @@ GEM
226
219
  ruby_llm (~> 1.9)
227
220
  zeitwerk (~> 2)
228
221
  ruby_llm-schema (0.2.1)
229
- rufus-scheduler (3.9.2)
230
- fugit (~> 1.1, >= 1.11.1)
231
222
  strings (0.2.1)
232
223
  strings-ansi (~> 0.2)
233
224
  unicode-display_width (>= 1.5, < 3.0)
@@ -250,8 +241,6 @@ GEM
250
241
  pastel (~> 0.8)
251
242
  strings (~> 0.2.0)
252
243
  tty-screen (~> 0.8)
253
- tzinfo (2.0.6)
254
- concurrent-ruby (~> 1.0)
255
244
  unicode-display_width (2.6.0)
256
245
  unicode_utils (1.4.0)
257
246
  uri (1.1.1)
@@ -37,7 +37,6 @@ module LanguageOperator
37
37
  @workspace_path = ENV.fetch('WORKSPACE_PATH', '/workspace')
38
38
  @mode = ENV.fetch('AGENT_MODE', 'autonomous')
39
39
  @executor = nil
40
- @scheduler = nil
41
40
  end
42
41
 
43
42
  # Run the agent in its configured mode
@@ -93,12 +92,16 @@ module LanguageOperator
93
92
  @executor.run_loop
94
93
  end
95
94
 
96
- # Run in scheduled mode
95
+ # Run in scheduled mode (execute once - Kubernetes CronJob handles scheduling)
97
96
  #
98
97
  # @return [void]
99
98
  def run_scheduled
100
- @scheduler = Scheduler.new(self)
101
- @scheduler.start
99
+ logger.info('Agent running in scheduled mode without definition - executing goal once')
100
+
101
+ goal = ENV.fetch('AGENT_INSTRUCTIONS', 'Complete the assigned task')
102
+ execute_goal(goal)
103
+
104
+ logger.info('Scheduled execution completed - exiting')
102
105
  end
103
106
 
104
107
  # Run in reactive mode (HTTP server)
@@ -3,7 +3,6 @@
3
3
  require_relative 'agent/base'
4
4
  require_relative 'agent/executor'
5
5
  require_relative 'agent/task_executor'
6
- require_relative 'agent/scheduler'
7
6
  require_relative 'agent/web_server'
8
7
  require_relative 'dsl'
9
8
  require_relative 'logger'
@@ -147,6 +146,7 @@ module LanguageOperator
147
146
  # @param agent [LanguageOperator::Agent::Base] The agent instance
148
147
  # @param agent_def [LanguageOperator::Dsl::AgentDefinition] The agent definition
149
148
  # @return [void]
149
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
150
150
  def self.run_with_definition(agent, agent_def)
151
151
  agent.connect!
152
152
 
@@ -167,17 +167,24 @@ module LanguageOperator
167
167
  raise 'Agent definition must have either main block (DSL v1) or workflow (DSL v0)'
168
168
  end
169
169
  when 'scheduled', 'event-driven'
170
+ # Scheduled mode: Execute once and exit (Kubernetes CronJob handles scheduling)
171
+ logger.info('Agent running in scheduled mode - executing once',
172
+ agent_name: agent_def.name,
173
+ dsl_version: uses_dsl_v1 ? 'v1' : 'v0')
174
+
170
175
  if uses_dsl_v1
171
- # DSL v1: Schedule main block execution
172
- scheduler = LanguageOperator::Agent::Scheduler.new(agent)
173
- scheduler.start_with_main(agent_def)
176
+ # DSL v1: Execute main block once
177
+ execute_main_block(agent, agent_def)
174
178
  elsif uses_dsl_v0
175
- # DSL v0: Schedule workflow execution
176
- scheduler = LanguageOperator::Agent::Scheduler.new(agent)
177
- scheduler.start_with_workflow(agent_def)
179
+ # DSL v0: Execute workflow once
180
+ executor = LanguageOperator::Agent::Executor.new(agent)
181
+ executor.execute_workflow(agent_def)
178
182
  else
179
183
  raise 'Agent definition must have either main block (DSL v1) or workflow (DSL v0)'
180
184
  end
185
+
186
+ logger.info('Scheduled execution completed - exiting',
187
+ agent_name: agent_def.name)
181
188
  when 'reactive', 'http', 'webhook'
182
189
  # Start web server with webhooks, MCP tools, and chat endpoint
183
190
  web_server = LanguageOperator::Agent::WebServer.new(agent)
@@ -189,6 +196,7 @@ module LanguageOperator
189
196
  raise "Unknown agent mode: #{agent.mode}"
190
197
  end
191
198
  end
199
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
192
200
 
193
201
  # Execute main block (DSL v1) in autonomous mode
194
202
  #
@@ -357,24 +357,6 @@ module LanguageOperator
357
357
  "Agent:#{@name}"
358
358
  end
359
359
 
360
- def run_scheduled
361
- require 'rufus-scheduler'
362
-
363
- scheduler = Rufus::Scheduler.new
364
-
365
- logger.info('Scheduling agent',
366
- name: @name,
367
- cron: @schedule)
368
-
369
- scheduler.cron(@schedule) do
370
- logger.timed('Scheduled execution') do
371
- execute_objectives
372
- end
373
- end
374
-
375
- scheduler.join
376
- end
377
-
378
360
  def run_autonomous
379
361
  logger.info('Running agent in autonomous mode', name: @name)
380
362
  execute_objectives
@@ -2,7 +2,7 @@
2
2
  :openapi: 3.0.3
3
3
  :info:
4
4
  :title: Language Operator Agent API
5
- :version: 0.1.52
5
+ :version: 0.1.53
6
6
  :description: HTTP API endpoints exposed by Language Operator reactive agents
7
7
  :contact:
8
8
  :name: Language Operator
@@ -3,7 +3,7 @@
3
3
  "$id": "https://github.com/language-operator/language-operator-gem/schema/agent-dsl.json",
4
4
  "title": "Language Operator Agent DSL",
5
5
  "description": "Schema for defining autonomous AI agents using the Language Operator DSL",
6
- "version": "0.1.52",
6
+ "version": "0.1.53",
7
7
  "type": "object",
8
8
  "properties": {
9
9
  "name": {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LanguageOperator
4
- VERSION = '0.1.52'
4
+ VERSION = '0.1.53'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: language-operator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.52
4
+ version: 0.1.53
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Ryan
@@ -121,20 +121,6 @@ dependencies:
121
121
  - - "~>"
122
122
  - !ruby/object:Gem::Version
123
123
  version: '1.26'
124
- - !ruby/object:Gem::Dependency
125
- name: rufus-scheduler
126
- requirement: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - "~>"
129
- - !ruby/object:Gem::Version
130
- version: '3.9'
131
- type: :runtime
132
- prerelease: false
133
- version_requirements: !ruby/object:Gem::Requirement
134
- requirements:
135
- - - "~>"
136
- - !ruby/object:Gem::Version
137
- version: '3.9'
138
124
  - !ruby/object:Gem::Dependency
139
125
  name: faraday
140
126
  requirement: !ruby/object:Gem::Requirement
@@ -468,7 +454,6 @@ files:
468
454
  - lib/language_operator/agent/safety/manager.rb
469
455
  - lib/language_operator/agent/safety/rate_limiter.rb
470
456
  - lib/language_operator/agent/safety/safe_executor.rb
471
- - lib/language_operator/agent/scheduler.rb
472
457
  - lib/language_operator/agent/task_executor.rb
473
458
  - lib/language_operator/agent/telemetry.rb
474
459
  - lib/language_operator/agent/web_server.rb
@@ -1,253 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rufus-scheduler'
4
- require_relative 'executor'
5
- require_relative 'instrumentation'
6
- require_relative '../logger'
7
- require_relative '../loggable'
8
-
9
- module LanguageOperator
10
- module Agent
11
- # Task Scheduler
12
- #
13
- # Handles scheduled and event-driven task execution using rufus-scheduler.
14
- #
15
- # @example
16
- # scheduler = Scheduler.new(agent)
17
- # scheduler.start
18
- class Scheduler
19
- include LanguageOperator::Loggable
20
- include LanguageOperator::Agent::Instrumentation
21
-
22
- attr_reader :agent, :rufus_scheduler
23
-
24
- # Initialize the scheduler
25
- #
26
- # @param agent [LanguageOperator::Agent::Base] The agent instance
27
- def initialize(agent)
28
- @agent = agent
29
- @rufus_scheduler = Rufus::Scheduler.new
30
- @executor = Executor.new(agent)
31
-
32
- logger.debug('Scheduler initialized',
33
- workspace: @agent.workspace_path,
34
- servers: @agent.servers_info.length)
35
- end
36
-
37
- # Start the scheduler
38
- #
39
- # @return [void]
40
- def start
41
- logger.info('Agent starting in scheduled mode')
42
- logger.info("Workspace: #{@agent.workspace_path}")
43
- logger.info("Connected to #{@agent.servers_info.length} MCP server(s)")
44
-
45
- setup_schedules
46
- logger.info('Scheduler started, waiting for scheduled tasks')
47
- @rufus_scheduler.join
48
- end
49
-
50
- # Start the scheduler with a workflow definition
51
- #
52
- # @param agent_def [LanguageOperator::Dsl::AgentDefinition] The agent definition with workflow
53
- # @return [void]
54
- def start_with_workflow(agent_def)
55
- logger.info('Agent starting in scheduled mode with workflow',
56
- agent_name: agent_def.name,
57
- has_workflow: !agent_def.workflow.nil?)
58
- logger.info("Workspace: #{@agent.workspace_path}")
59
- logger.info("Connected to #{@agent.servers_info.length} MCP server(s)")
60
-
61
- # Extract schedule from agent definition and validate
62
- cron_schedule = agent_def.schedule
63
- if cron_schedule.nil? || cron_schedule.empty?
64
- raise ArgumentError,
65
- "Schedule required for scheduled mode agent '#{agent_def.name}'. " \
66
- 'Use schedule() method to provide a cron expression.'
67
- end
68
-
69
- logger.info('Scheduling workflow', cron: cron_schedule, agent: agent_def.name)
70
-
71
- @rufus_scheduler.cron(cron_schedule) do
72
- with_span('agent.scheduler.execute', attributes: {
73
- 'scheduler.cron_expression' => cron_schedule,
74
- 'agent.name' => agent_def.name,
75
- 'scheduler.task_type' => 'workflow'
76
- }) do
77
- logger.timed('Scheduled workflow execution') do
78
- logger.info('Executing scheduled workflow', agent: agent_def.name)
79
- result = @executor.execute_workflow(agent_def)
80
- result_text = result.is_a?(String) ? result : result.content
81
- preview = result_text[0..200]
82
- preview += '...' if result_text.length > 200
83
- logger.info('Workflow completed', result_preview: preview)
84
- end
85
- end
86
- end
87
-
88
- logger.info('Scheduler started, waiting for scheduled tasks')
89
- @rufus_scheduler.join
90
- end
91
-
92
- # Start the scheduler with a main block (DSL v1)
93
- #
94
- # @param agent_def [LanguageOperator::Dsl::AgentDefinition] The agent definition with main block
95
- # @return [void]
96
- def start_with_main(agent_def)
97
- logger.info('Agent starting in scheduled mode with main block',
98
- agent_name: agent_def.name,
99
- task_count: agent_def.tasks.size)
100
- logger.info("Workspace: #{@agent.workspace_path}")
101
- logger.info("Connected to #{@agent.servers_info.length} MCP server(s)")
102
-
103
- # Extract schedule from agent definition and validate
104
- cron_schedule = agent_def.schedule
105
- if cron_schedule.nil? || cron_schedule.empty?
106
- raise ArgumentError,
107
- "Schedule required for scheduled mode agent '#{agent_def.name}'. " \
108
- 'Use schedule() method to provide a cron expression.'
109
- end
110
-
111
- logger.info('Scheduling main block execution', cron: cron_schedule, agent: agent_def.name)
112
-
113
- # Create task executor with constraints config
114
- require_relative 'task_executor'
115
- config = build_executor_config(agent_def)
116
- task_executor = TaskExecutor.new(@agent, agent_def.tasks, config)
117
-
118
- @rufus_scheduler.cron(cron_schedule) do
119
- with_span('agent.scheduler.execute', attributes: {
120
- 'scheduler.cron_expression' => cron_schedule,
121
- 'agent.name' => agent_def.name,
122
- 'scheduler.task_type' => 'main_block'
123
- }) do
124
- logger.timed('Scheduled main block execution') do
125
- logger.info('Executing scheduled main block', agent: agent_def.name)
126
-
127
- # Get inputs from environment or default to empty hash
128
- inputs = {}
129
-
130
- # Execute main block
131
- result = agent_def.main.call(inputs, task_executor)
132
-
133
- logger.info('Main block completed', result: result)
134
- end
135
- end
136
- end
137
-
138
- logger.info('Scheduler started, waiting for scheduled tasks')
139
- @rufus_scheduler.join
140
- end
141
-
142
- # Stop the scheduler
143
- #
144
- # @return [void]
145
- def stop
146
- logger.info('Shutting down scheduler')
147
- @rufus_scheduler.shutdown
148
- logger.info('Scheduler stopped')
149
- end
150
-
151
- private
152
-
153
- def logger_component
154
- 'Agent::Scheduler'
155
- end
156
-
157
- # Setup schedules from config
158
- #
159
- # @return [void]
160
- def setup_schedules
161
- schedules = @agent.config.dig('agent', 'schedules') || []
162
-
163
- logger.debug('Loading schedules from config', count: schedules.length)
164
-
165
- if schedules.empty?
166
- logger.warn('No schedules configured, using default daily schedule')
167
- setup_default_schedule
168
- return
169
- end
170
-
171
- schedules.each do |schedule|
172
- add_schedule(schedule)
173
- end
174
-
175
- logger.info("#{schedules.length} schedule(s) configured")
176
- end
177
-
178
- # Add a single schedule
179
- #
180
- # @param schedule [Hash] Schedule configuration
181
- # @return [void]
182
- def add_schedule(schedule)
183
- cron = schedule['cron']
184
- task = schedule['task']
185
- agent_name = @agent.config.dig('agent', 'name')
186
-
187
- logger.info('Scheduling task', cron: cron, task: task[0..100])
188
-
189
- @rufus_scheduler.cron(cron) do
190
- with_span('agent.scheduler.execute', attributes: {
191
- 'scheduler.cron_expression' => cron,
192
- 'agent.name' => agent_name,
193
- 'scheduler.task_type' => 'scheduled'
194
- }) do
195
- logger.timed('Scheduled task execution') do
196
- logger.info('Executing scheduled task', task: task[0..100])
197
- result = @executor.execute(task)
198
- preview = result[0..200]
199
- preview += '...' if result.length > 200
200
- logger.info('Task completed', result_preview: preview)
201
- end
202
- end
203
- end
204
- end
205
-
206
- # Setup default daily schedule
207
- #
208
- # @return [void]
209
- def setup_default_schedule
210
- instructions = @agent.config.dig('agent', 'instructions') ||
211
- 'Check for updates and report status'
212
- agent_name = @agent.config.dig('agent', 'name')
213
- cron = '0 6 * * *'
214
-
215
- logger.info('Setting up default schedule', cron: cron,
216
- instructions: instructions[0..100])
217
-
218
- @rufus_scheduler.cron(cron) do
219
- with_span('agent.scheduler.execute', attributes: {
220
- 'scheduler.cron_expression' => cron,
221
- 'agent.name' => agent_name,
222
- 'scheduler.task_type' => 'default'
223
- }) do
224
- logger.timed('Daily task execution') do
225
- logger.info('Executing daily task')
226
- result = @executor.execute(instructions)
227
- preview = result[0..200]
228
- preview += '...' if result.length > 200
229
- logger.info('Daily task completed', result_preview: preview)
230
- end
231
- end
232
- end
233
-
234
- logger.info('Scheduled: Daily at 6:00 AM')
235
- end
236
-
237
- # Build executor configuration from agent definition constraints
238
- #
239
- # @param agent_def [LanguageOperator::Dsl::AgentDefinition] The agent definition
240
- # @return [Hash] Executor configuration
241
- def build_executor_config(agent_def)
242
- config = {}
243
-
244
- if agent_def.constraints
245
- config[:timeout] = agent_def.constraints[:timeout] if agent_def.constraints[:timeout]
246
- config[:max_retries] = agent_def.constraints[:max_retries] if agent_def.constraints[:max_retries]
247
- end
248
-
249
- config
250
- end
251
- end
252
- end
253
- end