legionio 1.4.47 → 1.4.48
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 +8 -0
- data/lib/legion/api/capacity.rb +45 -0
- data/lib/legion/api.rb +2 -0
- data/lib/legion/capacity/model.rb +64 -0
- data/lib/legion/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d7a08e02740d6be6ecb76c91c3ee6f3a2ed8f594801994e8005c1ec3e3a3b47c
|
|
4
|
+
data.tar.gz: c45901fb93e4e97063e3044eee0d5646a50447acb818055ff75428a6c5f490a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 23eb01d19c9e38ab19d0bf28f5f2f9d32cb7bd64a47abd1fa0833b2e7d7ba13963c62eda860b43d5745cc54d77e4bacd94086e4749351e0b3af0489dd7ee5dc5
|
|
7
|
+
data.tar.gz: f436f935e81de29c1364022aad090c0e7c8bd7f1ee9419a1b33aaf1bb66800ffa117f699047ed02f314498161db79ad82193863893f5f538e9e08cf0147272b1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.48] - 2026-03-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `Legion::Capacity::Model`: workforce capacity calculation (throughput, utilization, forecast, per-worker stats)
|
|
7
|
+
- `GET /api/capacity`: aggregate capacity across active workers
|
|
8
|
+
- `GET /api/capacity/forecast`: projected capacity with configurable growth rate and period
|
|
9
|
+
- `GET /api/capacity/workers`: per-worker capacity breakdown
|
|
10
|
+
|
|
3
11
|
## [1.4.47] - 2026-03-17
|
|
4
12
|
|
|
5
13
|
### Fixed
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../capacity/model'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
class API < Sinatra::Base
|
|
7
|
+
module Routes
|
|
8
|
+
module Capacity
|
|
9
|
+
def self.registered(app)
|
|
10
|
+
app.get '/api/capacity' do
|
|
11
|
+
workers = Routes::Capacity.fetch_worker_list
|
|
12
|
+
model = Legion::Capacity::Model.new(workers: workers)
|
|
13
|
+
json_response(model.aggregate)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
app.get '/api/capacity/forecast' do
|
|
17
|
+
workers = Routes::Capacity.fetch_worker_list
|
|
18
|
+
model = Legion::Capacity::Model.new(workers: workers)
|
|
19
|
+
forecast = model.forecast(
|
|
20
|
+
days: (params[:days] || 30).to_i,
|
|
21
|
+
growth_rate: (params[:growth_rate] || 0).to_f
|
|
22
|
+
)
|
|
23
|
+
json_response(forecast)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
app.get '/api/capacity/workers' do
|
|
27
|
+
workers = Routes::Capacity.fetch_worker_list
|
|
28
|
+
model = Legion::Capacity::Model.new(workers: workers)
|
|
29
|
+
json_response(model.per_worker_stats)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.fetch_worker_list
|
|
34
|
+
return [] unless defined?(Legion::Data::Model::DigitalWorker)
|
|
35
|
+
|
|
36
|
+
Legion::Data::Model::DigitalWorker.all.map do |w|
|
|
37
|
+
{ worker_id: w.worker_id, status: w.lifecycle_state }
|
|
38
|
+
end
|
|
39
|
+
rescue StandardError
|
|
40
|
+
[]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/legion/api.rb
CHANGED
|
@@ -29,6 +29,7 @@ require_relative 'api/rbac'
|
|
|
29
29
|
require_relative 'api/auth'
|
|
30
30
|
require_relative 'api/auth_worker'
|
|
31
31
|
require_relative 'api/auth_human'
|
|
32
|
+
require_relative 'api/capacity'
|
|
32
33
|
require_relative 'api/audit'
|
|
33
34
|
require_relative 'api/metrics'
|
|
34
35
|
|
|
@@ -102,6 +103,7 @@ module Legion
|
|
|
102
103
|
register Routes::Auth
|
|
103
104
|
register Routes::AuthWorker
|
|
104
105
|
register Routes::AuthHuman
|
|
106
|
+
register Routes::Capacity
|
|
105
107
|
register Routes::Audit
|
|
106
108
|
register Routes::Metrics
|
|
107
109
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Capacity
|
|
5
|
+
class Model
|
|
6
|
+
DEFAULTS = {
|
|
7
|
+
tasks_per_second: 10,
|
|
8
|
+
utilization_target: 0.7,
|
|
9
|
+
availability_hours: 24,
|
|
10
|
+
overhead_factor: 0.15
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
def initialize(workers:, config: {})
|
|
14
|
+
@workers = Array(workers)
|
|
15
|
+
@config = DEFAULTS.merge(config)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def aggregate
|
|
19
|
+
active = @workers.select { |w| active_worker?(w) }
|
|
20
|
+
tps = @config[:tasks_per_second]
|
|
21
|
+
{
|
|
22
|
+
total_workers: @workers.size,
|
|
23
|
+
active_workers: active.size,
|
|
24
|
+
max_throughput_tps: active.size * tps,
|
|
25
|
+
effective_throughput_tps: (active.size * tps * @config[:utilization_target]).round,
|
|
26
|
+
utilization_target: @config[:utilization_target],
|
|
27
|
+
availability_hours: @config[:availability_hours]
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def forecast(days: 30, growth_rate: 0.0)
|
|
32
|
+
current = aggregate
|
|
33
|
+
projected = (current[:active_workers] * (1 + (growth_rate * days / 30.0))).ceil
|
|
34
|
+
tps = @config[:tasks_per_second]
|
|
35
|
+
{
|
|
36
|
+
period_days: days,
|
|
37
|
+
growth_rate: growth_rate,
|
|
38
|
+
current_workers: current[:active_workers],
|
|
39
|
+
projected_workers: projected,
|
|
40
|
+
projected_max_tps: projected * tps,
|
|
41
|
+
projected_effective_tps: (projected * tps * @config[:utilization_target]).round
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def per_worker_stats
|
|
46
|
+
@workers.map do |w|
|
|
47
|
+
id = w[:worker_id] || w[:id] || 'unknown'
|
|
48
|
+
{
|
|
49
|
+
worker_id: id,
|
|
50
|
+
status: w[:status] || w[:lifecycle_state] || 'unknown',
|
|
51
|
+
capacity_tps: active_worker?(w) ? @config[:tasks_per_second] : 0
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def active_worker?(worker)
|
|
59
|
+
status = (worker[:status] || worker[:lifecycle_state]).to_s
|
|
60
|
+
%w[active running].include?(status)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
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.4.
|
|
4
|
+
version: 1.4.48
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -320,6 +320,7 @@ files:
|
|
|
320
320
|
- lib/legion/api/auth.rb
|
|
321
321
|
- lib/legion/api/auth_human.rb
|
|
322
322
|
- lib/legion/api/auth_worker.rb
|
|
323
|
+
- lib/legion/api/capacity.rb
|
|
323
324
|
- lib/legion/api/chains.rb
|
|
324
325
|
- lib/legion/api/coldstart.rb
|
|
325
326
|
- lib/legion/api/events.rb
|
|
@@ -348,6 +349,7 @@ files:
|
|
|
348
349
|
- lib/legion/audit.rb
|
|
349
350
|
- lib/legion/audit/hash_chain.rb
|
|
350
351
|
- lib/legion/audit/siem_export.rb
|
|
352
|
+
- lib/legion/capacity/model.rb
|
|
351
353
|
- lib/legion/catalog.rb
|
|
352
354
|
- lib/legion/chat/notification_bridge.rb
|
|
353
355
|
- lib/legion/chat/notification_queue.rb
|