legionio 1.6.36 → 1.6.37
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/.rubocop.yml +1 -1
- data/CHANGELOG.md +10 -0
- data/lib/legion/api/catalog.rb +1 -1
- data/lib/legion/api/openapi.rb +2 -1
- data/lib/legion/api/tbi_patterns.rb +219 -0
- data/lib/legion/api.rb +2 -0
- data/lib/legion/data/local_migrations/20250601000001_create_tbi_patterns.rb +21 -0
- data/lib/legion/data/models/tbi_pattern.rb +21 -0
- data/lib/legion/telemetry/open_inference.rb +56 -0
- data/lib/legion/version.rb +1 -1
- data/workflows/autofix-pipeline.yml +58 -0
- data/workflows/factory-develop-codegen.yml +25 -0
- data/workflows/mind-growth-build.yml +88 -0
- data/workflows/mind-growth-swarm-parallel-build.yml +42 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a187a818bb06029a4b03e5eb47c75af63e2fe85f997d157ce7f36dad924307b
|
|
4
|
+
data.tar.gz: 77005d16734961543c5cf5bd81d615f7387686e731725f510e2f56c72a49613a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 856b38fdb0200c467538ed0c7b79863be85d0a14ea44d0ae52f0e13a6f655089cfb4e905b94f42a89b893809c183b81366789bf531a1f1023b9ba0c44523920d
|
|
7
|
+
data.tar.gz: a23b1bc63408859994b6c315aa38f7dba5a43e5a64bfdd7d0e8f51dc9a3ce4eac96d12d0bf98a2241e9bd7c507953d9733bb4e6b857186f8e3789f2a99db3206
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.6.37] - 2026-03-30
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- TBI Patterns API: `POST /api/tbi/patterns/export`, `GET /api/tbi/patterns`, `GET /api/tbi/patterns/:id`, `PATCH /api/tbi/patterns/:id/score`, `GET /api/tbi/patterns/discover` (501 stub)
|
|
9
|
+
- TBI Pattern model and local migration (`create_tbi_patterns`)
|
|
10
|
+
- OpenInference telemetry integration (`Legion::Telemetry::OpenInference`)
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Governance lifecycle integration specs expanded and hardened
|
|
14
|
+
|
|
5
15
|
## [1.6.36] - 2026-03-29
|
|
6
16
|
|
|
7
17
|
### Added
|
data/lib/legion/api/catalog.rb
CHANGED
data/lib/legion/api/openapi.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'legion/json'
|
|
4
|
+
require 'legion/api/events'
|
|
4
5
|
|
|
5
6
|
module Legion
|
|
6
7
|
class API < Sinatra::Base
|
|
@@ -922,7 +923,7 @@ module Legion
|
|
|
922
923
|
summary: 'Get recent events from ring buffer',
|
|
923
924
|
operationId: 'getRecentEvents',
|
|
924
925
|
parameters: [
|
|
925
|
-
{ name: 'count', in: 'query', description: "Number of events (max #{Routes::Events::BUFFER_SIZE})",
|
|
926
|
+
{ name: 'count', in: 'query', description: "Number of events (max #{Legion::API::Routes::Events::BUFFER_SIZE})",
|
|
926
927
|
required: false, schema: { type: 'integer', default: 25 } }
|
|
927
928
|
],
|
|
928
929
|
responses: {
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'digest'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
class API < Sinatra::Base
|
|
7
|
+
module Routes
|
|
8
|
+
module TbiPatterns
|
|
9
|
+
MAX_DESCRIPTION_BYTES = 1024
|
|
10
|
+
MAX_PAYLOAD_SHAPE_BYTES = 65_536
|
|
11
|
+
VALID_TIERS = %w[tier1 tier2 tier3 tier4 tier5].freeze
|
|
12
|
+
|
|
13
|
+
def self.registered(app)
|
|
14
|
+
register_export(app)
|
|
15
|
+
register_discover(app)
|
|
16
|
+
register_all(app)
|
|
17
|
+
register_score(app)
|
|
18
|
+
register_fetch(app)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# POST /api/tbi/patterns/export — anonymously export a learned behavioral pattern
|
|
22
|
+
def self.register_export(app) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
23
|
+
app.post '/api/tbi/patterns/export' do
|
|
24
|
+
require_data!
|
|
25
|
+
body = parse_request_body
|
|
26
|
+
|
|
27
|
+
if body[:pattern_type].to_s.strip.empty?
|
|
28
|
+
Legion::Logging.warn 'API POST /api/tbi/patterns/export returned 422: pattern_type is required' if defined?(Legion::Logging)
|
|
29
|
+
halt 422, json_error('missing_field', 'pattern_type is required', status_code: 422)
|
|
30
|
+
end
|
|
31
|
+
if body[:description].to_s.strip.empty?
|
|
32
|
+
Legion::Logging.warn 'API POST /api/tbi/patterns/export returned 422: description is required' if defined?(Legion::Logging)
|
|
33
|
+
halt 422, json_error('missing_field', 'description is required', status_code: 422)
|
|
34
|
+
end
|
|
35
|
+
if body[:pattern_data].to_s.strip.empty?
|
|
36
|
+
Legion::Logging.warn 'API POST /api/tbi/patterns/export returned 422: pattern_data is required' if defined?(Legion::Logging)
|
|
37
|
+
halt 422, json_error('missing_field', 'pattern_data is required', status_code: 422)
|
|
38
|
+
end
|
|
39
|
+
if body[:tier].to_s.strip.empty?
|
|
40
|
+
Legion::Logging.warn 'API POST /api/tbi/patterns/export returned 422: tier is required' if defined?(Legion::Logging)
|
|
41
|
+
halt 422, json_error('missing_field', 'tier is required', status_code: 422)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if body[:description].to_s.bytesize > MAX_DESCRIPTION_BYTES
|
|
45
|
+
halt 422, json_error('field_too_large', "description exceeds #{MAX_DESCRIPTION_BYTES} bytes", status_code: 422)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
pattern_data_str = Routes::TbiPatterns.serialize_pattern_data(body[:pattern_data])
|
|
49
|
+
if pattern_data_str.bytesize > MAX_PAYLOAD_SHAPE_BYTES
|
|
50
|
+
halt 422, json_error('field_too_large', "pattern_data exceeds #{MAX_PAYLOAD_SHAPE_BYTES} bytes", status_code: 422)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
unless VALID_TIERS.include?(body[:tier].to_s)
|
|
54
|
+
halt 422, json_error('invalid_field', "tier must be one of: #{VALID_TIERS.join(', ')}", status_code: 422)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Anonymize: strip any identifying keys before persisting
|
|
58
|
+
anonymous_data = Routes::TbiPatterns.anonymize(body)
|
|
59
|
+
|
|
60
|
+
invocation_count = Routes::TbiPatterns.parse_integer(body[:invocation_count], 0)
|
|
61
|
+
success_rate = Routes::TbiPatterns.parse_float(body[:success_rate], 0.0)
|
|
62
|
+
quality_score = Routes::TbiPatterns.compute_quality(
|
|
63
|
+
invocation_count: invocation_count,
|
|
64
|
+
success_rate: success_rate,
|
|
65
|
+
tier: body[:tier].to_s
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
record = Legion::Data::Model::TbiPattern.create(
|
|
69
|
+
pattern_type: body[:pattern_type].to_s,
|
|
70
|
+
description: body[:description].to_s,
|
|
71
|
+
tier: body[:tier].to_s,
|
|
72
|
+
pattern_data: pattern_data_str,
|
|
73
|
+
quality_score: quality_score,
|
|
74
|
+
invocation_count: invocation_count,
|
|
75
|
+
success_rate: success_rate,
|
|
76
|
+
source_hash: anonymous_data[:source_hash]
|
|
77
|
+
)
|
|
78
|
+
Legion::Logging.info "API: exported TBI pattern id=#{record.id} tier=#{record.tier}" if defined?(Legion::Logging)
|
|
79
|
+
json_response(record.values, status_code: 201)
|
|
80
|
+
rescue StandardError => e
|
|
81
|
+
Legion::Logging.error "API POST /api/tbi/patterns/export: #{e.class} — #{e.message}" if defined?(Legion::Logging)
|
|
82
|
+
json_error('export_error', e.message, status_code: 500)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# GET /api/tbi/patterns/:id — fetch a single pattern by integer ID
|
|
87
|
+
def self.register_fetch(app)
|
|
88
|
+
app.get '/api/tbi/patterns/:id' do
|
|
89
|
+
require_data!
|
|
90
|
+
id_val = params[:id].to_i
|
|
91
|
+
halt 422, json_error('invalid_id', 'id must be a positive integer', status_code: 422) if id_val <= 0
|
|
92
|
+
|
|
93
|
+
record = Legion::Data::Model::TbiPattern.first(id: id_val)
|
|
94
|
+
halt 404, json_error('not_found', "TBI pattern #{params[:id]} not found", status_code: 404) unless record
|
|
95
|
+
|
|
96
|
+
json_response(record.values)
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
Legion::Logging.error "API GET /api/tbi/patterns/#{params[:id]}: #{e.class} — #{e.message}" if defined?(Legion::Logging)
|
|
99
|
+
json_error('fetch_error', e.message, status_code: 500)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# GET /api/tbi/patterns — list patterns with optional tier/type filter
|
|
104
|
+
def self.register_all(app)
|
|
105
|
+
app.get '/api/tbi/patterns' do
|
|
106
|
+
require_data!
|
|
107
|
+
dataset = Legion::Data::Model::TbiPattern.order(Sequel.desc(:quality_score))
|
|
108
|
+
dataset = dataset.where(tier: params[:tier]) if params[:tier]
|
|
109
|
+
dataset = dataset.where(pattern_type: params[:type]) if params[:type]
|
|
110
|
+
json_collection(dataset)
|
|
111
|
+
rescue StandardError => e
|
|
112
|
+
Legion::Logging.error "API GET /api/tbi/patterns: #{e.class} — #{e.message}" if defined?(Legion::Logging)
|
|
113
|
+
json_error('list_error', e.message, status_code: 500)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# PATCH /api/tbi/patterns/:id/score — update quality score with new usage metadata
|
|
118
|
+
def self.register_score(app)
|
|
119
|
+
app.patch '/api/tbi/patterns/:id/score' do
|
|
120
|
+
require_data!
|
|
121
|
+
id_val = params[:id].to_i
|
|
122
|
+
halt 422, json_error('invalid_id', 'id must be a positive integer', status_code: 422) if id_val <= 0
|
|
123
|
+
|
|
124
|
+
record = Legion::Data::Model::TbiPattern.first(id: id_val)
|
|
125
|
+
halt 404, json_error('not_found', "TBI pattern #{params[:id]} not found", status_code: 404) unless record
|
|
126
|
+
|
|
127
|
+
body = parse_request_body
|
|
128
|
+
invocation_count = Routes::TbiPatterns.parse_integer(body[:invocation_count], record.invocation_count)
|
|
129
|
+
success_rate = Routes::TbiPatterns.parse_float(body[:success_rate], record.success_rate)
|
|
130
|
+
quality_score = Routes::TbiPatterns.compute_quality(
|
|
131
|
+
invocation_count: invocation_count,
|
|
132
|
+
success_rate: success_rate,
|
|
133
|
+
tier: record.tier
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
record.update(
|
|
137
|
+
invocation_count: invocation_count,
|
|
138
|
+
success_rate: success_rate,
|
|
139
|
+
quality_score: quality_score
|
|
140
|
+
)
|
|
141
|
+
Legion::Logging.info "API: rescored TBI pattern id=#{record.id} quality=#{quality_score}" if defined?(Legion::Logging)
|
|
142
|
+
json_response(record.values)
|
|
143
|
+
rescue StandardError => e
|
|
144
|
+
Legion::Logging.error "API PATCH /api/tbi/patterns/#{params[:id]}/score: #{e.class} — #{e.message}" if defined?(Legion::Logging)
|
|
145
|
+
json_error('score_error', e.message, status_code: 500)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# GET /api/tbi/patterns/discover — cross-instance pattern discovery (P3/TBI Phase 6)
|
|
150
|
+
# TODO: implement cross-instance discovery
|
|
151
|
+
def self.register_discover(app)
|
|
152
|
+
app.get '/api/tbi/patterns/discover' do
|
|
153
|
+
halt 501, json_error('not_implemented', 'cross-instance pattern discovery is not yet available', status_code: 501)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# --- helpers ---
|
|
158
|
+
|
|
159
|
+
# Anonymize pattern export: remove instance-identifying fields, compute a
|
|
160
|
+
# one-way hash for deduplication without fingerprinting.
|
|
161
|
+
def self.anonymize(body)
|
|
162
|
+
identifying_keys = %i[node_id instance_id hostname ip_address worker_id]
|
|
163
|
+
sanitized = body.reject { |k, _v| identifying_keys.include?(k.to_sym) }
|
|
164
|
+
# Remove both string and symbol variants
|
|
165
|
+
sanitized = sanitized.reject { |k, _v| identifying_keys.map(&:to_s).include?(k.to_s) }
|
|
166
|
+
|
|
167
|
+
salt_source = "#{body[:pattern_type]}:#{body[:tier]}:#{body[:description]}"
|
|
168
|
+
source_hash = Digest::SHA256.hexdigest(salt_source)[0, 16]
|
|
169
|
+
|
|
170
|
+
sanitized.merge(source_hash: source_hash)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def self.serialize_pattern_data(pattern_data)
|
|
174
|
+
return pattern_data.to_s if pattern_data.is_a?(String)
|
|
175
|
+
|
|
176
|
+
Legion::JSON.dump(pattern_data)
|
|
177
|
+
rescue StandardError
|
|
178
|
+
Legion::JSON.dump(pattern_data.to_s)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def self.compute_quality(invocation_count:, success_rate:, tier:)
|
|
182
|
+
# tier weight: higher tiers (closer to tier5) earn a modest bonus
|
|
183
|
+
tier_num = tier.to_s.gsub(/[^0-9]/, '').to_i.clamp(1, 5)
|
|
184
|
+
tier_weight = tier_num / 5.0
|
|
185
|
+
|
|
186
|
+
count_score = [invocation_count.to_f / 100.0, 1.0].min
|
|
187
|
+
success_score = success_rate.to_f.clamp(0.0, 1.0)
|
|
188
|
+
|
|
189
|
+
((count_score * 0.4) + (success_score * 0.5) + (tier_weight * 0.1)).round(4)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Parse an integer from user input; return default if blank or invalid.
|
|
193
|
+
def self.parse_integer(value, default)
|
|
194
|
+
return default if value.nil?
|
|
195
|
+
return default if value.to_s.strip.empty?
|
|
196
|
+
raise ArgumentError, 'not numeric' unless value.to_s =~ /\A-?\d+\z/
|
|
197
|
+
|
|
198
|
+
[value.to_i, 0].max
|
|
199
|
+
rescue ArgumentError
|
|
200
|
+
default
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Parse a float from user input; return default if blank or invalid.
|
|
204
|
+
def self.parse_float(value, default)
|
|
205
|
+
return default if value.nil?
|
|
206
|
+
return default if value.to_s.strip.empty?
|
|
207
|
+
raise ArgumentError, 'not numeric' unless value.to_s =~ /\A-?\d+(\.\d+)?\z/
|
|
208
|
+
|
|
209
|
+
value.to_f.clamp(0.0, 1.0)
|
|
210
|
+
rescue ArgumentError
|
|
211
|
+
default
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
private_class_method :register_export, :register_fetch, :register_all,
|
|
215
|
+
:register_score, :register_discover
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
data/lib/legion/api.rb
CHANGED
|
@@ -52,6 +52,7 @@ require_relative 'api/router'
|
|
|
52
52
|
require_relative 'api/library_routes'
|
|
53
53
|
require_relative 'api/sync_dispatch'
|
|
54
54
|
require_relative 'api/lex_dispatch'
|
|
55
|
+
require_relative 'api/tbi_patterns'
|
|
55
56
|
require_relative 'api/graphql' if defined?(GraphQL)
|
|
56
57
|
|
|
57
58
|
module Legion
|
|
@@ -176,6 +177,7 @@ module Legion
|
|
|
176
177
|
register Routes::Absorbers
|
|
177
178
|
register Routes::Codegen
|
|
178
179
|
register Routes::Logs
|
|
180
|
+
register Routes::TbiPatterns
|
|
179
181
|
register Routes::GraphQL if defined?(Routes::GraphQL)
|
|
180
182
|
|
|
181
183
|
use Legion::API::Middleware::RequestLogger
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table?(:tbi_patterns) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :pattern_type, null: false
|
|
8
|
+
String :description, null: false
|
|
9
|
+
String :tier, null: false
|
|
10
|
+
# TEXT column holds JSON-encoded behavioral pattern data (up to 64KB)
|
|
11
|
+
String :pattern_data, text: true, null: false
|
|
12
|
+
Float :quality_score, null: false, default: 0.0
|
|
13
|
+
Integer :invocation_count, null: false, default: 0
|
|
14
|
+
Float :success_rate, null: false, default: 0.0
|
|
15
|
+
# one-way SHA-256 prefix derived from pattern_type+tier+description; not reversible to the submitting instance
|
|
16
|
+
String :source_hash
|
|
17
|
+
Time :created_at, null: false
|
|
18
|
+
Time :updated_at, null: false
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
if defined?(Sequel)
|
|
4
|
+
module Legion
|
|
5
|
+
module Data
|
|
6
|
+
module Model
|
|
7
|
+
class TbiPattern < Sequel::Model(:tbi_patterns)
|
|
8
|
+
plugin :timestamps, update_on_create: true
|
|
9
|
+
|
|
10
|
+
def validate
|
|
11
|
+
super
|
|
12
|
+
errors.add(:pattern_type, 'is required') if !pattern_type || pattern_type.to_s.strip.empty?
|
|
13
|
+
errors.add(:description, 'is required') if !description || description.to_s.strip.empty?
|
|
14
|
+
errors.add(:pattern_data, 'is required') if !pattern_data || pattern_data.to_s.strip.empty?
|
|
15
|
+
errors.add(:tier, 'is required') if !tier || tier.to_s.strip.empty?
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -141,6 +141,51 @@ module Legion
|
|
|
141
141
|
Legion::Telemetry.with_span("agent.#{name}", kind: :internal, attributes: attrs, &)
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
+
def retriever_span(name:, query: nil, top_k: nil, &)
|
|
145
|
+
unless open_inference_enabled?
|
|
146
|
+
return yield(nil) if block_given?
|
|
147
|
+
|
|
148
|
+
return
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
attrs = base_attrs('RETRIEVER').merge('retriever.name' => name)
|
|
152
|
+
attrs['retriever.top_k'] = top_k if top_k
|
|
153
|
+
attrs['input.value'] = truncate_value(query.to_s) if query && include_io?
|
|
154
|
+
|
|
155
|
+
Legion::Telemetry.with_span("retriever.#{name}", kind: :client, attributes: attrs, &)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def reranker_span(model:, query: nil, top_k: nil, &)
|
|
159
|
+
unless open_inference_enabled?
|
|
160
|
+
return yield(nil) if block_given?
|
|
161
|
+
|
|
162
|
+
return
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
attrs = base_attrs('RERANKER').merge('reranker.model_name' => model)
|
|
166
|
+
attrs['reranker.top_k'] = top_k if top_k
|
|
167
|
+
attrs['input.value'] = truncate_value(query.to_s) if query && include_io?
|
|
168
|
+
|
|
169
|
+
Legion::Telemetry.with_span("reranker.#{model}", kind: :internal, attributes: attrs, &)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def guardrail_span(name:, input: nil)
|
|
173
|
+
unless open_inference_enabled?
|
|
174
|
+
return yield(nil) if block_given?
|
|
175
|
+
|
|
176
|
+
return
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
attrs = base_attrs('GUARDRAIL').merge('guardrail.name' => name)
|
|
180
|
+
attrs['input.value'] = truncate_value(input.to_s) if input && include_io?
|
|
181
|
+
|
|
182
|
+
Legion::Telemetry.with_span("guardrail.#{name}", kind: :internal, attributes: attrs) do |span|
|
|
183
|
+
result = yield(span)
|
|
184
|
+
annotate_guardrail_result(span, result) if span && result.is_a?(Hash)
|
|
185
|
+
result
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
144
189
|
def truncate_value(str, max: nil)
|
|
145
190
|
limit = max || truncate_limit
|
|
146
191
|
str.length > limit ? str[0...limit] : str
|
|
@@ -181,6 +226,17 @@ module Legion
|
|
|
181
226
|
Legion::Logging.debug "OpenInference#annotate_eval_result failed: #{e.message}" if defined?(Legion::Logging)
|
|
182
227
|
nil
|
|
183
228
|
end
|
|
229
|
+
|
|
230
|
+
def annotate_guardrail_result(span, result)
|
|
231
|
+
return unless span.respond_to?(:set_attribute)
|
|
232
|
+
|
|
233
|
+
span.set_attribute('guardrail.passed', result[:passed]) unless result[:passed].nil?
|
|
234
|
+
span.set_attribute('guardrail.score', result[:score]) unless result[:score].nil?
|
|
235
|
+
span.set_attribute('output.value', truncate_value(result[:explanation].to_s)) if include_io? && result[:explanation]
|
|
236
|
+
rescue StandardError => e
|
|
237
|
+
Legion::Logging.debug "OpenInference#annotate_guardrail_result failed: #{e.message}" if defined?(Legion::Logging)
|
|
238
|
+
nil
|
|
239
|
+
end
|
|
184
240
|
end
|
|
185
241
|
end
|
|
186
242
|
end
|
data/lib/legion/version.rb
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: autofix-pipeline
|
|
2
|
+
version: 0.1.0
|
|
3
|
+
description: >
|
|
4
|
+
Log event triage → GitHub issue → LLM fix → PR creation.
|
|
5
|
+
Triggered by batched exception log events.
|
|
6
|
+
|
|
7
|
+
requires:
|
|
8
|
+
- lex-autofix
|
|
9
|
+
- lex-github
|
|
10
|
+
|
|
11
|
+
relationships:
|
|
12
|
+
- name: triage-to-diagnose
|
|
13
|
+
trigger:
|
|
14
|
+
extension: autofix
|
|
15
|
+
runner: triage
|
|
16
|
+
function: batch_triage
|
|
17
|
+
action:
|
|
18
|
+
extension: autofix
|
|
19
|
+
runner: diagnose
|
|
20
|
+
function: check_github
|
|
21
|
+
conditions:
|
|
22
|
+
all:
|
|
23
|
+
- fact: success
|
|
24
|
+
operator: equal
|
|
25
|
+
value: true
|
|
26
|
+
|
|
27
|
+
- name: diagnose-to-fix
|
|
28
|
+
trigger:
|
|
29
|
+
extension: autofix
|
|
30
|
+
runner: diagnose
|
|
31
|
+
function: check_github
|
|
32
|
+
action:
|
|
33
|
+
extension: autofix
|
|
34
|
+
runner: fix
|
|
35
|
+
function: attempt_fix
|
|
36
|
+
conditions:
|
|
37
|
+
all:
|
|
38
|
+
- fact: success
|
|
39
|
+
operator: equal
|
|
40
|
+
value: true
|
|
41
|
+
- fact: action
|
|
42
|
+
operator: not_equal
|
|
43
|
+
value: skipped
|
|
44
|
+
|
|
45
|
+
- name: fix-to-ship
|
|
46
|
+
trigger:
|
|
47
|
+
extension: autofix
|
|
48
|
+
runner: fix
|
|
49
|
+
function: attempt_fix
|
|
50
|
+
action:
|
|
51
|
+
extension: autofix
|
|
52
|
+
runner: ship
|
|
53
|
+
function: ship
|
|
54
|
+
conditions:
|
|
55
|
+
all:
|
|
56
|
+
- fact: success
|
|
57
|
+
operator: equal
|
|
58
|
+
value: true
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: factory-develop-codegen
|
|
2
|
+
version: 0.1.0
|
|
3
|
+
description: >
|
|
4
|
+
Factory develop stage delegates code generation to lex-codegen.
|
|
5
|
+
Each spec requirement becomes a codegen task.
|
|
6
|
+
|
|
7
|
+
requires:
|
|
8
|
+
- lex-factory
|
|
9
|
+
- lex-codegen
|
|
10
|
+
|
|
11
|
+
relationships:
|
|
12
|
+
- name: develop-to-codegen
|
|
13
|
+
trigger:
|
|
14
|
+
extension: factory
|
|
15
|
+
runner: factory
|
|
16
|
+
function: run_pipeline
|
|
17
|
+
action:
|
|
18
|
+
extension: codegen
|
|
19
|
+
runner: from_gap
|
|
20
|
+
function: generate
|
|
21
|
+
conditions:
|
|
22
|
+
all:
|
|
23
|
+
- fact: success
|
|
24
|
+
operator: equal
|
|
25
|
+
value: true
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
name: mind-growth-build
|
|
2
|
+
version: 0.1.0
|
|
3
|
+
description: >
|
|
4
|
+
Extension build pipeline: scaffold → implement → test → validate → register.
|
|
5
|
+
Runs locally on the build node. Task relationships provide observability
|
|
6
|
+
and conditional retry on test failure.
|
|
7
|
+
|
|
8
|
+
requires:
|
|
9
|
+
- lex-mind-growth
|
|
10
|
+
- lex-codegen
|
|
11
|
+
- lex-eval
|
|
12
|
+
- lex-exec
|
|
13
|
+
|
|
14
|
+
relationships:
|
|
15
|
+
- name: scaffold-to-implement
|
|
16
|
+
trigger:
|
|
17
|
+
extension: mind_growth
|
|
18
|
+
runner: builder
|
|
19
|
+
function: scaffold_stage
|
|
20
|
+
action:
|
|
21
|
+
extension: mind_growth
|
|
22
|
+
runner: builder
|
|
23
|
+
function: implement_stage
|
|
24
|
+
conditions:
|
|
25
|
+
all:
|
|
26
|
+
- fact: success
|
|
27
|
+
operator: equal
|
|
28
|
+
value: true
|
|
29
|
+
|
|
30
|
+
- name: implement-to-test
|
|
31
|
+
trigger:
|
|
32
|
+
extension: mind_growth
|
|
33
|
+
runner: builder
|
|
34
|
+
function: implement_stage
|
|
35
|
+
action:
|
|
36
|
+
extension: mind_growth
|
|
37
|
+
runner: builder
|
|
38
|
+
function: test_stage
|
|
39
|
+
conditions:
|
|
40
|
+
all:
|
|
41
|
+
- fact: success
|
|
42
|
+
operator: equal
|
|
43
|
+
value: true
|
|
44
|
+
|
|
45
|
+
- name: test-pass-to-validate
|
|
46
|
+
trigger:
|
|
47
|
+
extension: mind_growth
|
|
48
|
+
runner: builder
|
|
49
|
+
function: test_stage
|
|
50
|
+
action:
|
|
51
|
+
extension: mind_growth
|
|
52
|
+
runner: builder
|
|
53
|
+
function: validate_stage
|
|
54
|
+
conditions:
|
|
55
|
+
all:
|
|
56
|
+
- fact: success
|
|
57
|
+
operator: equal
|
|
58
|
+
value: true
|
|
59
|
+
|
|
60
|
+
- name: test-fail-to-implement-retry
|
|
61
|
+
trigger:
|
|
62
|
+
extension: mind_growth
|
|
63
|
+
runner: builder
|
|
64
|
+
function: test_stage
|
|
65
|
+
action:
|
|
66
|
+
extension: mind_growth
|
|
67
|
+
runner: builder
|
|
68
|
+
function: implement_stage
|
|
69
|
+
conditions:
|
|
70
|
+
all:
|
|
71
|
+
- fact: success
|
|
72
|
+
operator: equal
|
|
73
|
+
value: false
|
|
74
|
+
|
|
75
|
+
- name: validate-to-register
|
|
76
|
+
trigger:
|
|
77
|
+
extension: mind_growth
|
|
78
|
+
runner: builder
|
|
79
|
+
function: validate_stage
|
|
80
|
+
action:
|
|
81
|
+
extension: mind_growth
|
|
82
|
+
runner: builder
|
|
83
|
+
function: register_stage
|
|
84
|
+
conditions:
|
|
85
|
+
all:
|
|
86
|
+
- fact: success
|
|
87
|
+
operator: equal
|
|
88
|
+
value: true
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: mind-growth-swarm-parallel-build
|
|
2
|
+
version: 0.1.0
|
|
3
|
+
description: >
|
|
4
|
+
Swarm-orchestrated parallel build: create swarm → build proposals → complete.
|
|
5
|
+
|
|
6
|
+
requires:
|
|
7
|
+
- lex-mind-growth
|
|
8
|
+
- lex-swarm
|
|
9
|
+
|
|
10
|
+
relationships:
|
|
11
|
+
- name: create-to-build
|
|
12
|
+
trigger:
|
|
13
|
+
extension: mind_growth
|
|
14
|
+
runner: swarm_builder
|
|
15
|
+
function: create_build_swarm
|
|
16
|
+
action:
|
|
17
|
+
extension: mind_growth
|
|
18
|
+
runner: swarm_builder
|
|
19
|
+
function: execute_parallel_build
|
|
20
|
+
conditions:
|
|
21
|
+
all:
|
|
22
|
+
- fact: success
|
|
23
|
+
operator: equal
|
|
24
|
+
value: true
|
|
25
|
+
- fact: charter_type
|
|
26
|
+
operator: equal
|
|
27
|
+
value: parallel_build
|
|
28
|
+
|
|
29
|
+
- name: build-to-complete
|
|
30
|
+
trigger:
|
|
31
|
+
extension: mind_growth
|
|
32
|
+
runner: swarm_builder
|
|
33
|
+
function: execute_parallel_build
|
|
34
|
+
action:
|
|
35
|
+
extension: swarm
|
|
36
|
+
runner: swarm
|
|
37
|
+
function: complete_swarm
|
|
38
|
+
conditions:
|
|
39
|
+
all:
|
|
40
|
+
- fact: success
|
|
41
|
+
operator: equal
|
|
42
|
+
value: true
|
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.6.
|
|
4
|
+
version: 1.6.37
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -513,6 +513,7 @@ files:
|
|
|
513
513
|
- lib/legion/api/stats.rb
|
|
514
514
|
- lib/legion/api/sync_dispatch.rb
|
|
515
515
|
- lib/legion/api/tasks.rb
|
|
516
|
+
- lib/legion/api/tbi_patterns.rb
|
|
516
517
|
- lib/legion/api/tenants.rb
|
|
517
518
|
- lib/legion/api/token.rb
|
|
518
519
|
- lib/legion/api/traces.rb
|
|
@@ -759,8 +760,10 @@ files:
|
|
|
759
760
|
- lib/legion/compliance/phi_erasure.rb
|
|
760
761
|
- lib/legion/compliance/phi_tag.rb
|
|
761
762
|
- lib/legion/context.rb
|
|
763
|
+
- lib/legion/data/local_migrations/20250601000001_create_tbi_patterns.rb
|
|
762
764
|
- lib/legion/data/local_migrations/20260319000001_create_extension_catalog.rb
|
|
763
765
|
- lib/legion/data/local_migrations/20260319000002_create_extension_permissions.rb
|
|
766
|
+
- lib/legion/data/models/tbi_pattern.rb
|
|
764
767
|
- lib/legion/digital_worker.rb
|
|
765
768
|
- lib/legion/digital_worker/airb.rb
|
|
766
769
|
- lib/legion/digital_worker/lifecycle.rb
|
|
@@ -872,7 +875,11 @@ files:
|
|
|
872
875
|
- public/workflow/index.html
|
|
873
876
|
- scripts/rollout-ci-workflow.sh
|
|
874
877
|
- scripts/sync-github-labels-topics.sh
|
|
878
|
+
- workflows/autofix-pipeline.yml
|
|
875
879
|
- workflows/autonomous-github-lifecycle.yml
|
|
880
|
+
- workflows/factory-develop-codegen.yml
|
|
881
|
+
- workflows/mind-growth-build.yml
|
|
882
|
+
- workflows/mind-growth-swarm-parallel-build.yml
|
|
876
883
|
homepage: https://github.com/LegionIO/LegionIO
|
|
877
884
|
licenses:
|
|
878
885
|
- Apache-2.0
|