lex-swarm 0.1.1 → 0.1.2
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 +1 -1
- data/lib/legion/extensions/swarm/actors/orphan_sweeper.rb +38 -0
- data/lib/legion/extensions/swarm/helpers/sub_agent.rb +82 -0
- data/lib/legion/extensions/swarm/runners/spawn_child.rb +17 -0
- data/lib/legion/extensions/swarm/version.rb +1 -1
- data/lib/legion/extensions/swarm.rb +2 -0
- data/spec/legion/extensions/swarm/actors/orphan_sweeper_spec.rb +41 -0
- data/spec/legion/extensions/swarm/helpers/sub_agent_spec.rb +67 -0
- data/spec/legion/extensions/swarm/runners/spawn_child_spec.rb +22 -0
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57a1719c0d8b1b40310d6d060c85d6a1e3f7ed54cd35b6de22eae1a63bd79361
|
|
4
|
+
data.tar.gz: ed02e732a76958b725dc538bd66e9a1c1605f3a5343f725262e61415e6def46e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98f84a0186ecfcaa0ba734e506f5d71f604759012aec2104490d3d5b2dd74f9ebe28e1c987ea8a4752e298a581f6584990ce830e1c575696f68c783a265fdd31
|
|
7
|
+
data.tar.gz: f1edc98f2e926bb94998fd4cce549d4d817d44b2a52e7466552324298ef186e8b1366cd27f4eb5bf0c8344ce9c9de0dca7091d4c25fb4df13a0a178ac964ed8e
|
data/Gemfile
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Swarm
|
|
6
|
+
module Actor
|
|
7
|
+
class OrphanSweeper < Legion::Extensions::Actors::Every
|
|
8
|
+
def time = 300
|
|
9
|
+
def run_now? = false
|
|
10
|
+
def use_runner? = false
|
|
11
|
+
def check_subtask? = false
|
|
12
|
+
def generate_task? = false
|
|
13
|
+
|
|
14
|
+
def action(**_opts)
|
|
15
|
+
return { swept: 0 } unless defined?(Legion::Data)
|
|
16
|
+
|
|
17
|
+
tasks = Legion::Data.connection[:tasks]
|
|
18
|
+
orphans = tasks.exclude(parent_id: nil)
|
|
19
|
+
.exclude(status: %w[complete failed])
|
|
20
|
+
.all
|
|
21
|
+
.select do |t|
|
|
22
|
+
parent = tasks.where(id: t[:parent_id]).first
|
|
23
|
+
parent.nil? || %w[complete failed].include?(parent[:status])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
orphans.each do |orphan|
|
|
27
|
+
tasks.where(id: orphan[:id]).update(status: 'failed')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
{ swept: orphans.size }
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
{ swept: 0, error: e.message }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Swarm
|
|
6
|
+
module Helpers
|
|
7
|
+
module SubAgent
|
|
8
|
+
def spawn(runner:, function:, payload:, parent_task_id:, **)
|
|
9
|
+
return { success: false, reason: :data_unavailable } unless defined?(Legion::Data::Model::Task)
|
|
10
|
+
|
|
11
|
+
parent = Legion::Data::Model::Task[parent_task_id]
|
|
12
|
+
return { success: false, reason: :parent_not_found } unless parent
|
|
13
|
+
|
|
14
|
+
new_depth = (parent.respond_to?(:depth) ? parent.depth.to_i : 0) + 1
|
|
15
|
+
|
|
16
|
+
return { success: false, reason: :depth_exceeded } if new_depth > max_depth
|
|
17
|
+
return { success: false, reason: :concurrent_exceeded } if concurrent_children_count > max_concurrent
|
|
18
|
+
return { success: false, reason: :per_parent_exceeded } if children_of(parent_task_id) > max_per_parent
|
|
19
|
+
|
|
20
|
+
if defined?(Legion::Ingress)
|
|
21
|
+
result = Legion::Ingress.run(
|
|
22
|
+
payload: payload,
|
|
23
|
+
runner_class: runner,
|
|
24
|
+
function: function,
|
|
25
|
+
source: 'sub_agent',
|
|
26
|
+
generate_task: true,
|
|
27
|
+
check_subtask: false
|
|
28
|
+
)
|
|
29
|
+
{ success: true, task_id: result[:task_id], depth: new_depth }
|
|
30
|
+
else
|
|
31
|
+
{ success: false, reason: :ingress_unavailable }
|
|
32
|
+
end
|
|
33
|
+
rescue StandardError => e
|
|
34
|
+
{ success: false, reason: :error, message: e.message }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def max_depth
|
|
40
|
+
Legion::Settings.dig(:swarm, :max_depth) || 3
|
|
41
|
+
rescue StandardError
|
|
42
|
+
3
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def max_concurrent
|
|
46
|
+
Legion::Settings.dig(:swarm, :max_concurrent) || 20
|
|
47
|
+
rescue StandardError
|
|
48
|
+
20
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def max_per_parent
|
|
52
|
+
Legion::Settings.dig(:swarm, :max_per_parent) || 10
|
|
53
|
+
rescue StandardError
|
|
54
|
+
10
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def concurrent_children_count
|
|
58
|
+
return 0 unless defined?(Legion::Data)
|
|
59
|
+
|
|
60
|
+
Legion::Data.connection[:tasks]
|
|
61
|
+
.exclude(parent_id: nil)
|
|
62
|
+
.exclude(status: %w[complete failed])
|
|
63
|
+
.count
|
|
64
|
+
rescue StandardError
|
|
65
|
+
0
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def children_of(parent_task_id)
|
|
69
|
+
return 0 unless defined?(Legion::Data)
|
|
70
|
+
|
|
71
|
+
Legion::Data.connection[:tasks]
|
|
72
|
+
.where(parent_id: parent_task_id)
|
|
73
|
+
.exclude(status: %w[complete failed])
|
|
74
|
+
.count
|
|
75
|
+
rescue StandardError
|
|
76
|
+
0
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Swarm
|
|
6
|
+
module Runners
|
|
7
|
+
module SpawnChild
|
|
8
|
+
include Helpers::SubAgent if defined?(Helpers::SubAgent)
|
|
9
|
+
|
|
10
|
+
def spawn_child(runner:, function:, payload:, parent_task_id:, **)
|
|
11
|
+
spawn(runner: runner, function: function, payload: payload, parent_task_id: parent_task_id)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
require 'legion/extensions/swarm/version'
|
|
4
4
|
require 'legion/extensions/swarm/helpers/charter'
|
|
5
5
|
require 'legion/extensions/swarm/helpers/swarm_store'
|
|
6
|
+
require 'legion/extensions/swarm/helpers/sub_agent'
|
|
6
7
|
require 'legion/extensions/swarm/runners/swarm'
|
|
8
|
+
require 'legion/extensions/swarm/runners/spawn_child'
|
|
7
9
|
|
|
8
10
|
module Legion
|
|
9
11
|
module Extensions
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Actors
|
|
6
|
+
unless defined?(Every)
|
|
7
|
+
class Every # rubocop:disable Lint/EmptyClass
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
$LOADED_FEATURES << 'legion/extensions/actors/every'
|
|
15
|
+
|
|
16
|
+
require_relative '../../../../../lib/legion/extensions/swarm/actors/orphan_sweeper'
|
|
17
|
+
|
|
18
|
+
RSpec.describe Legion::Extensions::Swarm::Actor::OrphanSweeper do
|
|
19
|
+
subject(:actor) { described_class.new }
|
|
20
|
+
|
|
21
|
+
describe '#time' do
|
|
22
|
+
it 'returns 300 seconds' do
|
|
23
|
+
expect(actor.time).to eq(300)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe '#run_now?' do
|
|
28
|
+
it 'returns false' do
|
|
29
|
+
expect(actor.run_now?).to be false
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe '#action' do
|
|
34
|
+
context 'when Legion::Data is not available' do
|
|
35
|
+
it 'returns zero swept' do
|
|
36
|
+
result = actor.action
|
|
37
|
+
expect(result[:swept]).to eq(0)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Legion::Extensions::Swarm::Helpers::SubAgent do
|
|
6
|
+
let(:helper_instance) { Class.new { include Legion::Extensions::Swarm::Helpers::SubAgent }.new }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
stub_const('Legion::Settings', Module.new do
|
|
10
|
+
def self.dig(*keys)
|
|
11
|
+
case keys
|
|
12
|
+
when %i[swarm max_depth] then 3
|
|
13
|
+
when %i[swarm max_concurrent] then 20
|
|
14
|
+
when %i[swarm max_per_parent] then 10
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#max_depth' do
|
|
21
|
+
it 'returns configured max depth' do
|
|
22
|
+
expect(helper_instance.send(:max_depth)).to eq(3)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#max_concurrent' do
|
|
27
|
+
it 'returns configured max concurrent' do
|
|
28
|
+
expect(helper_instance.send(:max_concurrent)).to eq(20)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe '#max_per_parent' do
|
|
33
|
+
it 'returns configured max per parent' do
|
|
34
|
+
expect(helper_instance.send(:max_per_parent)).to eq(10)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#spawn' do
|
|
39
|
+
context 'when legion-data is not available' do
|
|
40
|
+
it 'returns failure' do
|
|
41
|
+
result = helper_instance.spawn(runner: 'SomeRunner', function: 'do_thing',
|
|
42
|
+
payload: {}, parent_task_id: 1)
|
|
43
|
+
expect(result[:success]).to be false
|
|
44
|
+
expect(result[:reason]).to eq(:data_unavailable)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context 'when depth would be exceeded' do
|
|
49
|
+
let(:parent_task) { double('Task', depth: 3, id: 1) }
|
|
50
|
+
|
|
51
|
+
before do
|
|
52
|
+
pt = parent_task
|
|
53
|
+
task_model = Class.new do
|
|
54
|
+
define_method(:[]) { |_id| pt }
|
|
55
|
+
end.new
|
|
56
|
+
stub_const('Legion::Data::Model::Task', task_model)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'returns depth_exceeded' do
|
|
60
|
+
result = helper_instance.spawn(runner: 'SomeRunner', function: 'do_thing',
|
|
61
|
+
payload: {}, parent_task_id: 1)
|
|
62
|
+
expect(result[:success]).to be false
|
|
63
|
+
expect(result[:reason]).to eq(:depth_exceeded)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Legion::Extensions::Swarm::Runners::SpawnChild do
|
|
6
|
+
let(:runner_instance) { Class.new { include Legion::Extensions::Swarm::Runners::SpawnChild }.new }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
stub_const('Legion::Settings', Module.new do
|
|
10
|
+
def self.dig(*_keys) = nil
|
|
11
|
+
end)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#spawn_child' do
|
|
15
|
+
it 'delegates to SubAgent.spawn and returns result' do
|
|
16
|
+
result = runner_instance.spawn_child(runner: 'R', function: 'f',
|
|
17
|
+
payload: {}, parent_task_id: 1)
|
|
18
|
+
expect(result).to be_a(Hash)
|
|
19
|
+
expect(result).to have_key(:success)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-swarm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -33,16 +33,22 @@ files:
|
|
|
33
33
|
- Gemfile
|
|
34
34
|
- lex-swarm.gemspec
|
|
35
35
|
- lib/legion/extensions/swarm.rb
|
|
36
|
+
- lib/legion/extensions/swarm/actors/orphan_sweeper.rb
|
|
36
37
|
- lib/legion/extensions/swarm/actors/stale_check.rb
|
|
37
38
|
- lib/legion/extensions/swarm/client.rb
|
|
38
39
|
- lib/legion/extensions/swarm/helpers/charter.rb
|
|
40
|
+
- lib/legion/extensions/swarm/helpers/sub_agent.rb
|
|
39
41
|
- lib/legion/extensions/swarm/helpers/swarm_store.rb
|
|
42
|
+
- lib/legion/extensions/swarm/runners/spawn_child.rb
|
|
40
43
|
- lib/legion/extensions/swarm/runners/swarm.rb
|
|
41
44
|
- lib/legion/extensions/swarm/version.rb
|
|
45
|
+
- spec/legion/extensions/swarm/actors/orphan_sweeper_spec.rb
|
|
42
46
|
- spec/legion/extensions/swarm/actors/stale_check_spec.rb
|
|
43
47
|
- spec/legion/extensions/swarm/client_spec.rb
|
|
44
48
|
- spec/legion/extensions/swarm/helpers/charter_spec.rb
|
|
49
|
+
- spec/legion/extensions/swarm/helpers/sub_agent_spec.rb
|
|
45
50
|
- spec/legion/extensions/swarm/helpers/swarm_store_spec.rb
|
|
51
|
+
- spec/legion/extensions/swarm/runners/spawn_child_spec.rb
|
|
46
52
|
- spec/legion/extensions/swarm/runners/swarm_spec.rb
|
|
47
53
|
- spec/spec_helper.rb
|
|
48
54
|
homepage: https://github.com/LegionIO/lex-swarm
|