legionio 1.7.6 → 1.7.8
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/CHANGELOG.md +19 -0
- data/lib/legion/api/default_settings.rb +48 -0
- data/lib/legion/api/llm.rb +5 -2
- data/lib/legion/api.rb +1 -0
- data/lib/legion/cli/check_command.rb +4 -2
- data/lib/legion/context.rb +18 -0
- data/lib/legion/extensions/actors/base.rb +6 -3
- data/lib/legion/extensions/actors/every.rb +4 -3
- data/lib/legion/extensions/actors/loop.rb +1 -1
- data/lib/legion/extensions/actors/poll.rb +4 -4
- data/lib/legion/extensions/actors/subscription.rb +12 -9
- data/lib/legion/extensions/core.rb +1 -1
- data/lib/legion/extensions/helpers/logger.rb +3 -62
- data/lib/legion/extensions/helpers/task.rb +4 -2
- data/lib/legion/extensions/transport.rb +3 -2
- data/lib/legion/ingress.rb +12 -8
- data/lib/legion/runner.rb +34 -19
- data/lib/legion/service.rb +22 -10
- data/lib/legion/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6741515579e2c24f64b301136e023fb6ddd73b27f41779caace7df02cd15f667
|
|
4
|
+
data.tar.gz: c90f66af48bd6705b58c6c31344dd0a9486b8519cc48b98f3b1965d2571527db
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5409680125318327c866c9f59dde8ce2b7b5a33cea5082bf2d654c763184c25e4658a24ef43d4b292f12546b314692da1b5d71ca6e25ca55e6095ea376d66d5f
|
|
7
|
+
data.tar.gz: 68a9358035224f600dfc673272d2cc82ecdfc7236a7225a2974721ad61b081571dbb190c77d25c8442bc3ff13c7cea44274b8331fd2942213a714167c56017c4
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.7.8] - 2026-04-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `Legion::API::Settings` module with registered defaults via `merge_settings('api', ...)`, matching the pattern used by all other LegionIO gems
|
|
9
|
+
- Puma `persistent_timeout` (20s) and `first_data_timeout` (30s) now configurable via `Settings[:api][:puma]`
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Removed all inline `||` and `.fetch(..., default)` fallbacks for API settings in `service.rb` and `check_command.rb` — defaults now guaranteed by `merge_settings`
|
|
13
|
+
|
|
14
|
+
## [1.7.7] - 2026-04-01
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Integrated legion-logging 1.4.3 Helper refactor: all log output now uses structured segment tagging, colored exception output, and thread-local task context
|
|
18
|
+
- Slimmed `Extensions::Helpers::Logger` to thin override; `derive_component_type`, `lex_gem_name`, `gem_spec_for_lex`, `log_lex_name` now live in legion-logging gem
|
|
19
|
+
- Added `handle_runner_exception` for runner-specific exception handling (TaskLog publish + HandledTask raise)
|
|
20
|
+
- Added `Legion::Context.with_task_context` and `.current_task_context` for thread-local task propagation
|
|
21
|
+
- Wrapped all 5 dispatch paths (Runner.run, Subscription#dispatch_runner, Base#runner, Ingress local/remote) with context propagation
|
|
22
|
+
- Migrated 13 `log.log_exception` call sites to `handle_exception` across actors, core, transport, and task helpers
|
|
23
|
+
|
|
5
24
|
## [1.7.6] - 2026-04-01
|
|
6
25
|
|
|
7
26
|
### Changed
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'sinatra/base'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
class API < Sinatra::Base
|
|
7
|
+
module Settings
|
|
8
|
+
def self.default
|
|
9
|
+
{
|
|
10
|
+
enabled: true,
|
|
11
|
+
port: 4567,
|
|
12
|
+
bind: '0.0.0.0',
|
|
13
|
+
puma: puma_defaults,
|
|
14
|
+
bind_retries: 3,
|
|
15
|
+
bind_retry_wait: 2,
|
|
16
|
+
tls: tls_defaults
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.puma_defaults
|
|
21
|
+
{
|
|
22
|
+
min_threads: 10,
|
|
23
|
+
max_threads: 16,
|
|
24
|
+
persistent_timeout: 20,
|
|
25
|
+
first_data_timeout: 30
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.tls_defaults
|
|
30
|
+
{
|
|
31
|
+
enabled: false
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
begin
|
|
39
|
+
Legion::Settings.merge_settings('api', Legion::API::Settings.default) if Legion.const_defined?('Settings', false)
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
if Legion.const_defined?('Logging', false) && Legion::Logging.respond_to?(:fatal)
|
|
42
|
+
Legion::Logging.fatal(e.message)
|
|
43
|
+
Legion::Logging.fatal(e.backtrace)
|
|
44
|
+
else
|
|
45
|
+
puts e.message
|
|
46
|
+
puts e.backtrace
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/legion/api/llm.rb
CHANGED
|
@@ -366,8 +366,11 @@ module Legion
|
|
|
366
366
|
stream do |out|
|
|
367
367
|
full_text = +''
|
|
368
368
|
pipeline_response = executor.call_stream do |chunk|
|
|
369
|
-
|
|
370
|
-
|
|
369
|
+
text = chunk.respond_to?(:content) ? chunk.content.to_s : chunk.to_s
|
|
370
|
+
next if text.empty?
|
|
371
|
+
|
|
372
|
+
full_text << text
|
|
373
|
+
out << "event: text-delta\ndata: #{Legion::JSON.dump({ delta: text })}\n\n"
|
|
371
374
|
end
|
|
372
375
|
|
|
373
376
|
if pipeline_response.tools.is_a?(Array) && !pipeline_response.tools.empty?
|
data/lib/legion/api.rb
CHANGED
|
@@ -260,8 +260,10 @@ module Legion
|
|
|
260
260
|
|
|
261
261
|
def check_api(_options)
|
|
262
262
|
require 'legion/api'
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
api_settings = Legion::Settings[:api]
|
|
264
|
+
port = api_settings[:port]
|
|
265
|
+
configured_bind = api_settings[:bind]
|
|
266
|
+
bind = %w[127.0.0.1 localhost ::1].include?(configured_bind) ? configured_bind : '127.0.0.1'
|
|
265
267
|
|
|
266
268
|
Legion::API.set :port, port
|
|
267
269
|
Legion::API.set :bind, bind
|
data/lib/legion/context.rb
CHANGED
|
@@ -51,6 +51,24 @@ module Legion
|
|
|
51
51
|
Legion::Logging.debug "[Context] session cleared: #{ctx&.session_id}" if defined?(Legion::Logging)
|
|
52
52
|
Thread.current[:legion_session_context] = nil
|
|
53
53
|
end
|
|
54
|
+
|
|
55
|
+
def current_task_context
|
|
56
|
+
Thread.current[:legion_context]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def with_task_context(message)
|
|
60
|
+
previous = Thread.current[:legion_context]
|
|
61
|
+
Thread.current[:legion_context] = {
|
|
62
|
+
task_id: message[:task_id],
|
|
63
|
+
conversation_id: message[:conversation_id],
|
|
64
|
+
chain_id: message[:chain_id],
|
|
65
|
+
function: message[:function],
|
|
66
|
+
runner_class: message[:runner_class]
|
|
67
|
+
}.compact
|
|
68
|
+
yield
|
|
69
|
+
ensure
|
|
70
|
+
Thread.current[:legion_context] = previous
|
|
71
|
+
end
|
|
54
72
|
end
|
|
55
73
|
end
|
|
56
74
|
end
|
|
@@ -16,9 +16,12 @@ module Legion
|
|
|
16
16
|
define_dsl_accessor :remote_invocable, default: true
|
|
17
17
|
|
|
18
18
|
def runner
|
|
19
|
-
|
|
19
|
+
with_log_context(function) do
|
|
20
|
+
Legion::Runner.run(runner_class: runner_class, function: function,
|
|
21
|
+
check_subtask: check_subtask?, generate_task: generate_task?)
|
|
22
|
+
end
|
|
20
23
|
rescue StandardError => e
|
|
21
|
-
|
|
24
|
+
handle_exception(e)
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
def manual
|
|
@@ -31,7 +34,7 @@ module Legion
|
|
|
31
34
|
klass.send(func, **args)
|
|
32
35
|
end
|
|
33
36
|
rescue StandardError => e
|
|
34
|
-
|
|
37
|
+
handle_exception(e)
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
def function
|
|
@@ -24,7 +24,8 @@ module Legion
|
|
|
24
24
|
log.debug "[Every] tick: #{self.class}" if defined?(log)
|
|
25
25
|
skip_or_run { use_runner? ? runner : manual }
|
|
26
26
|
rescue StandardError => e
|
|
27
|
-
log.
|
|
27
|
+
log.error "[Every] tick failed for #{self.class}: #{e.class}: #{e.message}" if defined?(log)
|
|
28
|
+
handle_exception(e) if defined?(log)
|
|
28
29
|
ensure
|
|
29
30
|
@executing.make_false
|
|
30
31
|
end
|
|
@@ -35,7 +36,7 @@ module Legion
|
|
|
35
36
|
|
|
36
37
|
@timer.execute
|
|
37
38
|
rescue StandardError => e
|
|
38
|
-
|
|
39
|
+
handle_exception(e)
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def run_now?
|
|
@@ -52,7 +53,7 @@ module Legion
|
|
|
52
53
|
|
|
53
54
|
@timer.shutdown
|
|
54
55
|
rescue StandardError => e
|
|
55
|
-
|
|
56
|
+
handle_exception(e)
|
|
56
57
|
end
|
|
57
58
|
end
|
|
58
59
|
end
|
|
@@ -27,7 +27,7 @@ check_subtask: check_subtask? }}"
|
|
|
27
27
|
begin
|
|
28
28
|
skip_or_run { poll_cycle }
|
|
29
29
|
rescue StandardError => e
|
|
30
|
-
|
|
30
|
+
handle_exception(e, level: :fatal)
|
|
31
31
|
ensure
|
|
32
32
|
@executing.make_false
|
|
33
33
|
end
|
|
@@ -37,7 +37,7 @@ check_subtask: check_subtask? }}"
|
|
|
37
37
|
end
|
|
38
38
|
@timer.execute
|
|
39
39
|
rescue StandardError => e
|
|
40
|
-
|
|
40
|
+
handle_exception(e)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def poll_cycle
|
|
@@ -69,7 +69,7 @@ check_subtask: check_subtask? }}"
|
|
|
69
69
|
log.debug("#{self.class} result: #{results}")
|
|
70
70
|
results
|
|
71
71
|
rescue StandardError => e
|
|
72
|
-
|
|
72
|
+
handle_exception(e, level: :fatal)
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def cache_name
|
|
@@ -92,7 +92,7 @@ check_subtask: check_subtask? }}"
|
|
|
92
92
|
Legion::Logging.debug 'Cancelling Legion Poller'
|
|
93
93
|
@timer.shutdown
|
|
94
94
|
rescue StandardError => e
|
|
95
|
-
|
|
95
|
+
handle_exception(e)
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
end
|
|
@@ -25,7 +25,7 @@ module Legion
|
|
|
25
25
|
@queue = queue.new
|
|
26
26
|
@queue.channel.prefetch(prefetch) if defined? prefetch
|
|
27
27
|
rescue StandardError => e
|
|
28
|
-
|
|
28
|
+
handle_exception(e, level: :fatal)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def create_queue
|
|
@@ -83,12 +83,12 @@ module Legion
|
|
|
83
83
|
|
|
84
84
|
cancel if Legion::Settings[:client][:shutting_down]
|
|
85
85
|
rescue StandardError => e
|
|
86
|
-
|
|
86
|
+
handle_exception(e, lex: lex_name, fn: fn, routing_key: delivery_info.routing_key)
|
|
87
87
|
@queue.reject(delivery_info.delivery_tag) if manual_ack
|
|
88
88
|
end
|
|
89
89
|
log.info "[Subscription] prepared: #{lex_name}/#{runner_name}"
|
|
90
90
|
rescue StandardError => e
|
|
91
|
-
|
|
91
|
+
handle_exception(e, level: :fatal)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
def activate
|
|
@@ -175,7 +175,7 @@ module Legion
|
|
|
175
175
|
|
|
176
176
|
cancel if Legion::Settings[:client][:shutting_down]
|
|
177
177
|
rescue StandardError => e
|
|
178
|
-
|
|
178
|
+
handle_exception(e)
|
|
179
179
|
log.warn "[Subscription] nacking message for #{lex_name}/#{fn}"
|
|
180
180
|
@queue.reject(delivery_info.delivery_tag) if manual_ack
|
|
181
181
|
end
|
|
@@ -207,11 +207,14 @@ module Legion
|
|
|
207
207
|
|
|
208
208
|
def dispatch_runner(message, runner_cls, function, check_subtask, generate_task)
|
|
209
209
|
run_block = lambda {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
210
|
+
ctx = message.merge(runner_class: runner_cls.to_s, function: function.to_s)
|
|
211
|
+
Legion::Context.with_task_context(ctx) do
|
|
212
|
+
Legion::Runner.run(**message,
|
|
213
|
+
runner_class: runner_cls,
|
|
214
|
+
function: function,
|
|
215
|
+
check_subtask: check_subtask,
|
|
216
|
+
generate_task: generate_task)
|
|
217
|
+
end
|
|
215
218
|
}
|
|
216
219
|
|
|
217
220
|
if defined?(Legion::Telemetry::OpenInference)
|
|
@@ -235,7 +235,7 @@ module Legion
|
|
|
235
235
|
lex_class.const_set(:Data, Module.new { extend Legion::Extensions::Data })
|
|
236
236
|
end
|
|
237
237
|
rescue StandardError => e
|
|
238
|
-
|
|
238
|
+
handle_exception(e, lex: lex_name, operation: 'auto_generate_data')
|
|
239
239
|
end
|
|
240
240
|
end
|
|
241
241
|
end
|
|
@@ -9,78 +9,19 @@ module Legion
|
|
|
9
9
|
include Legion::Extensions::Helpers::Base
|
|
10
10
|
include Legion::Logging::Helper
|
|
11
11
|
|
|
12
|
-
def
|
|
13
|
-
|
|
14
|
-
log.log_exception(exception,
|
|
15
|
-
lex: log_lex_name,
|
|
16
|
-
component_type: derive_component_type,
|
|
17
|
-
gem_name: lex_gem_name,
|
|
18
|
-
lex_version: spec&.version&.to_s,
|
|
19
|
-
gem_path: spec&.full_gem_path,
|
|
20
|
-
source_code_uri: spec&.metadata&.[]('source_code_uri'),
|
|
21
|
-
handled: true,
|
|
22
|
-
payload_summary: opts.empty? ? nil : opts,
|
|
23
|
-
task_id: task_id)
|
|
12
|
+
def handle_runner_exception(exception, task_id: nil, **opts) # rubocop:disable Style/ArgumentsForwarding
|
|
13
|
+
handle_exception(exception, task_id: task_id, **opts) # rubocop:disable Style/ArgumentsForwarding
|
|
24
14
|
|
|
25
15
|
unless task_id.nil?
|
|
26
16
|
Legion::Transport::Messages::TaskLog.new(
|
|
27
17
|
task_id: task_id,
|
|
28
18
|
runner_class: to_s,
|
|
29
|
-
entry: {
|
|
30
|
-
exception: true,
|
|
31
|
-
message: exception.message,
|
|
32
|
-
**opts
|
|
33
|
-
}
|
|
19
|
+
entry: { exception: true, message: exception.message, **opts }
|
|
34
20
|
).publish
|
|
35
21
|
end
|
|
36
22
|
|
|
37
23
|
raise Legion::Exception::HandledTask
|
|
38
24
|
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
def derive_component_type
|
|
43
|
-
parts = respond_to?(:calling_class_array) ? calling_class_array : self.class.to_s.split('::')
|
|
44
|
-
match = parts.find { |p| Legion::Extensions::Helpers::Base::NAMESPACE_BOUNDARIES.include?(p) }
|
|
45
|
-
case match
|
|
46
|
-
when 'Runners' then :runner
|
|
47
|
-
when 'Actor', 'Actors' then :actor
|
|
48
|
-
when 'Transport' then :transport
|
|
49
|
-
when 'Helpers' then :helper
|
|
50
|
-
when 'Data' then :data
|
|
51
|
-
else :unknown
|
|
52
|
-
end
|
|
53
|
-
rescue StandardError
|
|
54
|
-
:unknown
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def lex_gem_name
|
|
58
|
-
base_name = log_lex_name
|
|
59
|
-
return nil unless base_name
|
|
60
|
-
|
|
61
|
-
"lex-#{base_name}"
|
|
62
|
-
rescue StandardError
|
|
63
|
-
nil
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def gem_spec_for_lex
|
|
67
|
-
name = lex_gem_name
|
|
68
|
-
return nil unless name
|
|
69
|
-
|
|
70
|
-
Gem::Specification.find_by_name(name)
|
|
71
|
-
rescue Gem::MissingSpecError
|
|
72
|
-
nil
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def log_lex_name
|
|
76
|
-
if respond_to?(:segments)
|
|
77
|
-
segments.join('-')
|
|
78
|
-
else
|
|
79
|
-
derive_log_tag
|
|
80
|
-
end
|
|
81
|
-
rescue StandardError
|
|
82
|
-
nil
|
|
83
|
-
end
|
|
84
25
|
end
|
|
85
26
|
end
|
|
86
27
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'base'
|
|
4
|
+
require_relative 'logger'
|
|
4
5
|
require 'legion/transport'
|
|
5
6
|
require 'legion/transport/messages/task_update'
|
|
6
7
|
require 'legion/transport/messages/task_log'
|
|
@@ -10,6 +11,7 @@ module Legion
|
|
|
10
11
|
module Helpers
|
|
11
12
|
module Task
|
|
12
13
|
include Legion::Extensions::Helpers::Base
|
|
14
|
+
include Legion::Extensions::Helpers::Logger
|
|
13
15
|
|
|
14
16
|
def generate_task_log(task_id:, function:, runner_class: to_s, **payload)
|
|
15
17
|
begin
|
|
@@ -19,7 +21,7 @@ module Legion
|
|
|
19
21
|
return true if Legion::Data::Model::TaskLog.insert(task_id: task_id, function_id: function_id, entry: Legion::JSON.dump(payload))
|
|
20
22
|
end
|
|
21
23
|
rescue StandardError => e
|
|
22
|
-
|
|
24
|
+
handle_exception(e, level: :warn)
|
|
23
25
|
end
|
|
24
26
|
Legion::Transport::Messages::TaskLog.new(task_id: task_id, runner_class: runner_class, function: function, entry: payload).publish
|
|
25
27
|
end
|
|
@@ -43,7 +45,7 @@ module Legion
|
|
|
43
45
|
end
|
|
44
46
|
Legion::Transport::Messages::TaskUpdate.new(**update_hash).publish
|
|
45
47
|
rescue StandardError => e
|
|
46
|
-
|
|
48
|
+
handle_exception(e, level: :fatal)
|
|
47
49
|
raise e
|
|
48
50
|
end
|
|
49
51
|
|
|
@@ -25,7 +25,8 @@ module Legion
|
|
|
25
25
|
auto_generate_messages
|
|
26
26
|
log.info "[Transport] built exchanges=#{@exchanges.count} queues=#{@queues.count} for #{lex_name}"
|
|
27
27
|
rescue StandardError => e
|
|
28
|
-
log.
|
|
28
|
+
log.error "[Transport] build failed for #{lex_name}"
|
|
29
|
+
handle_exception(e, lex: lex_name)
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def generate_base_modules
|
|
@@ -175,7 +176,7 @@ module Legion
|
|
|
175
176
|
to = to.is_a?(String) ? Kernel.const_get(to).new : to.new
|
|
176
177
|
to.bind(from, routing_key: routing_key)
|
|
177
178
|
rescue StandardError => e
|
|
178
|
-
|
|
179
|
+
handle_exception(e, level: :fatal, from: from, to: to, routing_key: routing_key)
|
|
179
180
|
end
|
|
180
181
|
|
|
181
182
|
def e_to_q
|
data/lib/legion/ingress.rb
CHANGED
|
@@ -82,17 +82,21 @@ module Legion
|
|
|
82
82
|
if local_runner?(rc)
|
|
83
83
|
Legion::Logging.debug "[Ingress] local short-circuit: #{rc}.#{fn}" if defined?(Legion::Logging)
|
|
84
84
|
klass = rc.is_a?(String) ? Kernel.const_get(rc) : rc
|
|
85
|
-
|
|
85
|
+
ctx = message.merge(runner_class: rc.to_s, function: fn.to_s)
|
|
86
|
+
return Legion::Context.with_task_context(ctx) { klass.send(fn.to_sym, **message) }
|
|
86
87
|
end
|
|
87
88
|
|
|
88
89
|
runner_block = lambda {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
ctx = message.merge(runner_class: rc.to_s, function: fn.to_s)
|
|
91
|
+
Legion::Context.with_task_context(ctx) do
|
|
92
|
+
Legion::Runner.run(
|
|
93
|
+
runner_class: rc,
|
|
94
|
+
function: fn,
|
|
95
|
+
check_subtask: check_subtask,
|
|
96
|
+
generate_task: generate_task,
|
|
97
|
+
**message
|
|
98
|
+
)
|
|
99
|
+
end
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
if defined?(Legion::Telemetry::OpenInference)
|
data/lib/legion/runner.rb
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'runner/log'
|
|
4
4
|
require_relative 'runner/status'
|
|
5
|
+
require_relative 'context'
|
|
5
6
|
require 'legion/transport'
|
|
6
7
|
require 'legion/transport/messages/check_subtask'
|
|
7
8
|
|
|
8
9
|
module Legion
|
|
9
10
|
module Runner
|
|
10
|
-
def self.run(runner_class:, function:, task_id: nil, args: nil, check_subtask: true, generate_task: true, parent_id: nil, master_id: nil, catch_exceptions: false, **opts) # rubocop:disable Layout/LineLength, Metrics/CyclomaticComplexity, Metrics/ParameterLists, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
11
|
+
def self.run(runner_class:, function:, task_id: nil, args: nil, check_subtask: true, generate_task: true, parent_id: nil, master_id: nil, catch_exceptions: false, **opts) # rubocop:disable Layout/LineLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/ParameterLists, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
11
12
|
started_at = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
12
13
|
lex_tag = derive_lex_tag(runner_class)
|
|
13
14
|
rlog = runner_logger(lex_tag)
|
|
@@ -31,24 +32,38 @@ module Legion
|
|
|
31
32
|
# result = Fiber.new { Fiber.yield runner_class.send(function, **args) }
|
|
32
33
|
raise 'No Function defined' if function.nil?
|
|
33
34
|
|
|
34
|
-
result =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
35
|
+
result = nil
|
|
36
|
+
status = nil
|
|
37
|
+
Legion::Context.with_task_context(opts.merge(task_id: task_id, function: function, runner_class: runner_class.to_s)) do
|
|
38
|
+
result = if runner_class.respond_to?(:with_log_context)
|
|
39
|
+
runner_class.with_log_context(function) { runner_class.send(function, **args) }
|
|
40
|
+
else
|
|
41
|
+
runner_class.send(function, **args)
|
|
42
|
+
end
|
|
43
|
+
rescue Legion::Exception::HandledTask => e
|
|
44
|
+
rlog.debug "[Runner] HandledTask raised in #{runner_class}##{function}: #{e.message}"
|
|
45
|
+
status = 'task.exception'
|
|
46
|
+
result = { error: {} }
|
|
47
|
+
rescue StandardError => e
|
|
48
|
+
rlog.error "[Runner] exception in #{runner_class}##{function}: #{e.message}"
|
|
49
|
+
status = 'task.exception'
|
|
50
|
+
result = { success: false, status: status, error: { message: e.message, backtrace: e.backtrace } }
|
|
51
|
+
if runner_class.respond_to?(:handle_runner_exception)
|
|
52
|
+
begin
|
|
53
|
+
runner_class.handle_runner_exception(e,
|
|
54
|
+
**opts,
|
|
55
|
+
runner_class: runner_class,
|
|
56
|
+
args: args,
|
|
57
|
+
function: function,
|
|
58
|
+
task_id: task_id,
|
|
59
|
+
generate_task: generate_task,
|
|
60
|
+
check_subtask: check_subtask)
|
|
61
|
+
rescue Legion::Exception::HandledTask => handled
|
|
62
|
+
rlog.debug "[Runner] HandledTask raised while handling exception in #{runner_class}##{function}: #{handled.message}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
raise e unless catch_exceptions
|
|
66
|
+
end
|
|
52
67
|
ensure
|
|
53
68
|
status = 'task.completed' if status.nil?
|
|
54
69
|
duration_ms = ((::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - started_at) * 1000).round
|
data/lib/legion/service.rb
CHANGED
|
@@ -142,8 +142,10 @@ module Legion
|
|
|
142
142
|
setup_metrics
|
|
143
143
|
setup_task_outcome_observer
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
require 'sinatra/base'
|
|
146
|
+
require 'legion/api/default_settings'
|
|
147
|
+
api_settings = Legion::Settings[:api]
|
|
148
|
+
@api_enabled = api && api_settings[:enabled]
|
|
147
149
|
setup_api if @api_enabled
|
|
148
150
|
setup_network_watchdog
|
|
149
151
|
Legion::Settings[:client][:ready] = true
|
|
@@ -264,38 +266,48 @@ module Legion
|
|
|
264
266
|
)
|
|
265
267
|
end
|
|
266
268
|
|
|
267
|
-
def setup_api
|
|
269
|
+
def setup_api # rubocop:disable Metrics/MethodLength
|
|
268
270
|
if @api_thread&.alive?
|
|
269
271
|
Legion::Logging.warn 'API already running, skipping duplicate setup_api call'
|
|
270
272
|
return
|
|
271
273
|
end
|
|
272
274
|
|
|
273
275
|
require 'legion/api'
|
|
274
|
-
api_settings = Legion::Settings[:api]
|
|
275
|
-
port = api_settings[:port]
|
|
276
|
-
bind = api_settings[:bind]
|
|
276
|
+
api_settings = Legion::Settings[:api]
|
|
277
|
+
port = api_settings[:port]
|
|
278
|
+
bind = api_settings[:bind]
|
|
277
279
|
|
|
278
280
|
Legion::API.set :port, port
|
|
279
281
|
Legion::API.set :bind, bind
|
|
280
282
|
Legion::API.set :server, :puma
|
|
281
283
|
Legion::API.set :environment, :production
|
|
282
284
|
|
|
285
|
+
puma_cfg = api_settings[:puma]
|
|
286
|
+
min_threads = puma_cfg[:min_threads]
|
|
287
|
+
max_threads = puma_cfg[:max_threads]
|
|
288
|
+
thread_spec = "#{min_threads}:#{max_threads}"
|
|
289
|
+
puma_timeouts = {
|
|
290
|
+
persistent_timeout: puma_cfg[:persistent_timeout],
|
|
291
|
+
first_data_timeout: puma_cfg[:first_data_timeout]
|
|
292
|
+
}.compact
|
|
293
|
+
|
|
283
294
|
tls_cfg = build_api_tls_config(api_settings)
|
|
284
295
|
if tls_cfg
|
|
285
296
|
Legion::API.set :ssl_bind_options, tls_cfg
|
|
286
|
-
Legion::API.set :server_settings, { quiet: true,
|
|
297
|
+
Legion::API.set :server_settings, { quiet: true, Threads: thread_spec, **puma_timeouts,
|
|
298
|
+
**ssl_server_settings(tls_cfg, bind, port) }
|
|
287
299
|
Legion::Logging.info "Starting Legion API (TLS) on #{bind}:#{port}"
|
|
288
300
|
else
|
|
289
301
|
require 'puma'
|
|
290
302
|
puma_log = ::Puma::LogWriter.new(StringIO.new, StringIO.new)
|
|
291
|
-
Legion::API.set :server_settings, { log_writer: puma_log, quiet: true }
|
|
303
|
+
Legion::API.set :server_settings, { log_writer: puma_log, quiet: true, Threads: thread_spec, **puma_timeouts }
|
|
292
304
|
Legion::Logging.info "Starting Legion API on #{bind}:#{port}"
|
|
293
305
|
end
|
|
294
306
|
|
|
295
307
|
@api_thread = Thread.new do
|
|
296
308
|
retries = 0
|
|
297
|
-
max_retries = api_settings
|
|
298
|
-
retry_wait = api_settings
|
|
309
|
+
max_retries = api_settings[:bind_retries]
|
|
310
|
+
retry_wait = api_settings[:bind_retry_wait]
|
|
299
311
|
|
|
300
312
|
begin
|
|
301
313
|
raise Errno::EADDRINUSE, "port #{port} already bound" if port_in_use?(bind, port)
|
data/lib/legion/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: legionio
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.7.
|
|
4
|
+
version: 1.7.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -472,6 +472,7 @@ files:
|
|
|
472
472
|
- lib/legion/api/codegen.rb
|
|
473
473
|
- lib/legion/api/coldstart.rb
|
|
474
474
|
- lib/legion/api/costs.rb
|
|
475
|
+
- lib/legion/api/default_settings.rb
|
|
475
476
|
- lib/legion/api/events.rb
|
|
476
477
|
- lib/legion/api/extensions.rb
|
|
477
478
|
- lib/legion/api/gaia.rb
|