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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b72b8c33768eb24380924cb9f33a342dd5e0e07fa1daaf151089de1d38e141e
4
- data.tar.gz: 790c2c25675b41bb049410a15f21585fc361a00963d771af73752b612c2d15e9
3
+ metadata.gz: 57a1719c0d8b1b40310d6d060c85d6a1e3f7ed54cd35b6de22eae1a63bd79361
4
+ data.tar.gz: ed02e732a76958b725dc538bd66e9a1c1605f3a5343f725262e61415e6def46e
5
5
  SHA512:
6
- metadata.gz: 9ca0b32d4b9c8af87086c777c71822c32228b195e0e1c7cb825c89c9a883dd97cee6e8815e228ca4d4a8a07ce1d31813eca91e09191405f06b45c9ed0c347453
7
- data.tar.gz: 6765b85f142bebdb96f537860d4984e56c762551d87da9b6fd3ab0a3b028c6f6fc3236e46b50bf445a020bf1d570c6fae3791af408c6cdc77d6ea77f68382c64
6
+ metadata.gz: 98f84a0186ecfcaa0ba734e506f5d71f604759012aec2104490d3d5b2dd74f9ebe28e1c987ea8a4752e298a581f6584990ce830e1c575696f68c783a265fdd31
7
+ data.tar.gz: f1edc98f2e926bb94998fd4cce549d4d817d44b2a52e7466552324298ef186e8b1366cd27f4eb5bf0c8344ce9c9de0dca7091d4c25fb4df13a0a178ac964ed8e
data/Gemfile CHANGED
@@ -8,4 +8,4 @@ gem 'rspec', '~> 3.13'
8
8
  gem 'rubocop', '~> 1.75', require: false
9
9
  gem 'rubocop-rspec', require: false
10
10
 
11
- gem 'legion-gaia', path: '../../legion-gaia'
11
+ gem 'legion-gaia'
@@ -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,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Swarm
6
- VERSION = '0.1.1'
6
+ VERSION = '0.1.2'
7
7
  end
8
8
  end
9
9
  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.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