legionio 1.9.36 → 1.9.38
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 +18 -1
- data/Gemfile +4 -1
- data/legionio.gemspec +1 -1
- data/lib/legion/cli/bootstrap_command.rb +1 -1
- data/lib/legion/cli/setup_command.rb +97 -1
- data/lib/legion/data/local_migrations/20260528000001_add_tbi_patterns_indexes.rb +17 -0
- data/lib/legion/extensions/actors/subscription.rb +18 -1
- data/lib/legion/guardrails.rb +6 -1
- data/lib/legion/service.rb +1 -0
- data/lib/legion/tools/embedding_cache/migrations/002_add_tool_name_index.rb +15 -0
- data/lib/legion/trace_search.rb +3 -0
- data/lib/legion/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24bc1b87a13d79e82092afab9dc8b49289e8ccd1ff87a328b8fbf1aae03d1a26
|
|
4
|
+
data.tar.gz: a042ab7323da0adf2247bfc0a81f964ed29b7d12c4c48c18703cb18bdbfc3ce2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0461e632dc1c9055c923742030767991205dd5479e72684dc2231548ba54a45806f6275f031c47c725527c9718a8cf4440bbea072bbbbb911baea96ea4fe004a
|
|
7
|
+
data.tar.gz: c2844f68ffd80a5c8ab24f7e1733bd93988b94b8d5d1f215c5e13d2a4be52e4828a195218be1f6a4f7ce01ed5d5beb46d22f12aa417ca19914dd71e449cd105f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [1.9.38] - 2026-05-30
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Gemspec: require legion-llm >= 0.10.1 (message translation, streaming, curator fixes required for Claude Code and Codex CLI agentic tool loops via vLLM)
|
|
7
|
+
|
|
8
|
+
## [1.9.37] - 2026-05-29
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- LLM: namespace API enabled by default — LegionIO now routes all `/v1/` and `/api/llm/` traffic
|
|
12
|
+
through `Namespaces::Registration` (Sinatra::Namespace, Phases 0-4 complete in legion-llm ≥ 0.8.50)
|
|
13
|
+
- CLI: `legion setup proxy-mode` (alias: `proxy`) writes `~/.codex/config.toml` and
|
|
14
|
+
`~/.claude/settings.json` env block so Codex CLI and Claude Code connect to LegionIO at
|
|
15
|
+
`http://localhost:4567` out of the box. Supports `--port`, `--host`, `--force`, `--json`.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- LLM: Anthropic namespace message translation now properly converts `tool_use`/`tool_result` content blocks to OpenAI format for vLLM dispatch (requires legion-llm ≥ 0.10.1)
|
|
19
|
+
- LLM: streaming tool_use blocks emitted inline with guaranteed ordering before `message_stop`
|
|
20
|
+
- LLM: curator preserves recent turns — no longer curates tool results from the current/previous turn
|
|
4
21
|
|
|
5
22
|
## [1.9.36] - 2026-05-22
|
|
6
23
|
|
data/Gemfile
CHANGED
|
@@ -19,8 +19,11 @@ gem 'legion-mcp', path: '../legion-mcp' if File.exist?(File.expand_path('../legi
|
|
|
19
19
|
gem 'legion-tty', path: '../legion-tty' if File.exist?(File.expand_path('../legion-tty', __dir__))
|
|
20
20
|
|
|
21
21
|
gem 'lex-apollo', path: '../extensions/lex-apollo' if File.exist?(File.expand_path('../extensions/lex-apollo', __dir__))
|
|
22
|
+
gem 'lex-lex', path: '../extensions/lex-lex' if File.exist?(File.expand_path('../extensions/lex-lex', __dir__))
|
|
22
23
|
gem 'lex-llm', path: '../extensions-ai/lex-llm' if File.exist?(File.expand_path('../extensions-ai/lex-llm', __dir__))
|
|
23
|
-
|
|
24
|
+
gem 'lex-llm-ledger', path: '../extensions-ai/lex-llm-ledger' if File.exist?(File.expand_path('../extensions-ai/lex-llm-ledger', __dir__))
|
|
25
|
+
# gem 'lex-microsoft_teams', path: '../extensions/lex-microsoft_teams' if File.exist?(File.expand_path('../extensions/lex-microsoft_teams', __dir__))
|
|
26
|
+
# gem 'lex-lex', path: '../extensions/lex-lex' if File.exist?(File.expand_path('../extensions/lex-lex', __dir__))
|
|
24
27
|
|
|
25
28
|
if File.exist?(File.expand_path('../extensions-identity/lex-identity-entra', __dir__))
|
|
26
29
|
gem 'lex-identity-entra', path: '../extensions-identity/lex-identity-entra'
|
data/legionio.gemspec
CHANGED
|
@@ -62,7 +62,7 @@ Gem::Specification.new do |spec|
|
|
|
62
62
|
|
|
63
63
|
spec.add_dependency 'legion-apollo', '>= 0.4.0'
|
|
64
64
|
spec.add_dependency 'legion-gaia', '>= 0.9.26'
|
|
65
|
-
spec.add_dependency 'legion-llm', '>= 0.
|
|
65
|
+
spec.add_dependency 'legion-llm', '>= 0.10.1'
|
|
66
66
|
spec.add_dependency 'legion-tty', '>= 0.5.4'
|
|
67
67
|
spec.add_dependency 'lex-node'
|
|
68
68
|
end
|
|
@@ -293,7 +293,7 @@ module Legion
|
|
|
293
293
|
|
|
294
294
|
def install_single_gem(name, gem_bin, out)
|
|
295
295
|
puts " Installing #{name}..." unless options[:json]
|
|
296
|
-
output, success = shell_capture("#{gem_bin} install #{name} --no-document")
|
|
296
|
+
output, success = shell_capture("#{gem_bin} install #{name} --no-document --clear-sources --source https://rubygems.org/")
|
|
297
297
|
if success
|
|
298
298
|
out.success(" #{name} installed") unless options[:json]
|
|
299
299
|
{ name: name, status: 'installed' }
|
|
@@ -157,6 +157,34 @@ module Legion
|
|
|
157
157
|
end
|
|
158
158
|
end
|
|
159
159
|
|
|
160
|
+
desc 'proxy-mode', 'Configure Codex CLI and Claude Code to use LegionIO as a local API proxy'
|
|
161
|
+
option :port, type: :numeric, default: 4567, desc: 'LegionIO API port'
|
|
162
|
+
option :host, type: :string, default: 'localhost', desc: 'LegionIO API host'
|
|
163
|
+
def proxy_mode
|
|
164
|
+
out = formatter
|
|
165
|
+
base_url = "http://#{options[:host]}:#{options[:port]}/v1"
|
|
166
|
+
written = []
|
|
167
|
+
skipped = []
|
|
168
|
+
|
|
169
|
+
write_codex_config(base_url, written, skipped)
|
|
170
|
+
write_claude_code_proxy_config(base_url, written, skipped)
|
|
171
|
+
|
|
172
|
+
if options[:json]
|
|
173
|
+
out.json(written: written, skipped: skipped, base_url: base_url)
|
|
174
|
+
else
|
|
175
|
+
out.spacer
|
|
176
|
+
out.success("LegionIO proxy mode configured (#{written.size} written, #{skipped.size} skipped)")
|
|
177
|
+
written.each { |f| puts " Written: #{f}" }
|
|
178
|
+
skipped.each { |f| puts " Skipped (already exists, use --force to overwrite): #{f}" }
|
|
179
|
+
out.spacer
|
|
180
|
+
puts " LegionIO API: #{base_url.sub('/v1', '')}"
|
|
181
|
+
puts ' Codex CLI: legion llm proxy (uses ~/.codex/config.toml)'
|
|
182
|
+
puts ' Claude Code: set ANTHROPIC_BASE_URL in your shell or ~/.claude/settings.json'
|
|
183
|
+
out.spacer
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
map 'proxy' => :proxy_mode
|
|
187
|
+
|
|
160
188
|
desc 'agentic', 'Install full cognitive stack (GAIA + LLM + Apollo + all agentic extensions)'
|
|
161
189
|
option :dry_run, type: :boolean, default: false, desc: 'Show what would be installed without installing'
|
|
162
190
|
def agentic
|
|
@@ -473,7 +501,7 @@ module Legion
|
|
|
473
501
|
|
|
474
502
|
def install_gem(name, gem_bin, out)
|
|
475
503
|
puts " Installing #{name}..." unless options[:json]
|
|
476
|
-
output = `#{gem_bin} install #{name} --no-document 2>&1`
|
|
504
|
+
output = `#{gem_bin} install #{name} --no-document --clear-sources --source https://rubygems.org/ 2>&1`
|
|
477
505
|
if $CHILD_STATUS.success?
|
|
478
506
|
out.success(" #{name} installed") unless options[:json]
|
|
479
507
|
{ name: name, status: 'installed' }
|
|
@@ -712,6 +740,74 @@ module Legion
|
|
|
712
740
|
end
|
|
713
741
|
{ name: 'VS Code', path: path, configured: configured }
|
|
714
742
|
end
|
|
743
|
+
|
|
744
|
+
def write_codex_config(base_url, written, skipped)
|
|
745
|
+
codex_dir = File.expand_path('~/.codex')
|
|
746
|
+
codex_path = File.join(codex_dir, 'config.toml')
|
|
747
|
+
|
|
748
|
+
if File.exist?(codex_path) && !options[:force]
|
|
749
|
+
skipped << codex_path
|
|
750
|
+
return
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
FileUtils.mkdir_p(codex_dir)
|
|
754
|
+
|
|
755
|
+
content = <<~TOML
|
|
756
|
+
model = "legionio"
|
|
757
|
+
model_provider = "legion"
|
|
758
|
+
|
|
759
|
+
[model_providers.legion]
|
|
760
|
+
name = "LegionIO"
|
|
761
|
+
env_key = "LEGION_API_KEY"
|
|
762
|
+
base_url = "#{base_url}"
|
|
763
|
+
wire_api = "responses"
|
|
764
|
+
TOML
|
|
765
|
+
|
|
766
|
+
File.write(codex_path, content)
|
|
767
|
+
written << codex_path
|
|
768
|
+
rescue StandardError => e
|
|
769
|
+
raise Thor::Error, "Failed to write #{codex_path}: #{e.message}"
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
def write_claude_code_proxy_config(base_url, written, skipped)
|
|
773
|
+
claude_dir = File.expand_path('~/.claude')
|
|
774
|
+
claude_path = File.join(claude_dir, 'settings.json')
|
|
775
|
+
|
|
776
|
+
existing = if File.exist?(claude_path)
|
|
777
|
+
begin
|
|
778
|
+
::JSON.parse(File.read(claude_path))
|
|
779
|
+
rescue ::JSON::ParserError
|
|
780
|
+
{}
|
|
781
|
+
end
|
|
782
|
+
else
|
|
783
|
+
{}
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
proxy_env = {
|
|
787
|
+
'ANTHROPIC_BASE_URL' => base_url.sub(%r{/v1$}, ''),
|
|
788
|
+
'ANTHROPIC_API_KEY' => 'legion',
|
|
789
|
+
'ANTHROPIC_AUTH_TOKEN' => 'legion',
|
|
790
|
+
'ANTHROPIC_DEFAULT_OPUS_MODEL' => 'legionio',
|
|
791
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL' => 'legionio',
|
|
792
|
+
'ANTHROPIC_DEFAULT_HAIKU_MODEL' => 'legionio'
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
current_env = existing['env'] || {}
|
|
796
|
+
|
|
797
|
+
already_set = proxy_env.all? { |k, v| current_env[k] == v }
|
|
798
|
+
if already_set && !options[:force]
|
|
799
|
+
skipped << claude_path
|
|
800
|
+
return
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
merged = existing.merge('env' => current_env.merge(proxy_env))
|
|
804
|
+
|
|
805
|
+
FileUtils.mkdir_p(claude_dir)
|
|
806
|
+
File.write(claude_path, ::JSON.pretty_generate(merged))
|
|
807
|
+
written << claude_path
|
|
808
|
+
rescue StandardError => e
|
|
809
|
+
raise Thor::Error, "Failed to write #{claude_path}: #{e.message}"
|
|
810
|
+
end
|
|
715
811
|
end
|
|
716
812
|
end
|
|
717
813
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
alter_table(:tbi_patterns) do
|
|
6
|
+
add_index :pattern_type, name: :idx_tbi_patterns_type
|
|
7
|
+
add_index :tier, name: :idx_tbi_patterns_tier
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
down do
|
|
12
|
+
alter_table(:tbi_patterns) do
|
|
13
|
+
drop_index :tier, name: :idx_tbi_patterns_tier
|
|
14
|
+
drop_index :pattern_type, name: :idx_tbi_patterns_type
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -65,8 +65,10 @@ module Legion
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def prepare # rubocop:disable Metrics/AbcSize
|
|
68
|
+
@dedicated_channel = create_dedicated_channel
|
|
68
69
|
@queue = queue.new
|
|
69
|
-
@queue
|
|
70
|
+
reassign_queue_channel(@queue, @dedicated_channel)
|
|
71
|
+
@dedicated_channel.prefetch(prefetch) if defined? prefetch
|
|
70
72
|
consumer_tag = "#{Legion::Settings[:client][:name]}_#{lex_name}_#{runner_name}_#{SecureRandom.uuid}"
|
|
71
73
|
@consumer = Bunny::Consumer.new(@queue.channel, @queue, consumer_tag, false, false)
|
|
72
74
|
@consumer.on_delivery do |delivery_info, metadata, payload|
|
|
@@ -298,6 +300,21 @@ module Legion
|
|
|
298
300
|
end
|
|
299
301
|
end
|
|
300
302
|
|
|
303
|
+
def create_dedicated_channel
|
|
304
|
+
s = Legion::Transport::Connection.session
|
|
305
|
+
raise IOError, 'transport session unavailable' unless s&.open?
|
|
306
|
+
|
|
307
|
+
settings = Legion::Transport::Connection.settings
|
|
308
|
+
s.create_channel(nil, settings[:channel][:default_worker_pool_size], false, 10)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def reassign_queue_channel(queue_instance, new_channel)
|
|
312
|
+
old_channel = queue_instance.channel
|
|
313
|
+
old_channel.deregister_queue(queue_instance) if old_channel.respond_to?(:deregister_queue)
|
|
314
|
+
queue_instance.instance_variable_set(:@channel, new_channel)
|
|
315
|
+
new_channel.register_queue(queue_instance) if new_channel.respond_to?(:register_queue)
|
|
316
|
+
end
|
|
317
|
+
|
|
301
318
|
def republish_with_retry_count(_delivery_info, metadata, payload, new_count)
|
|
302
319
|
headers = (metadata&.headers || {}).dup
|
|
303
320
|
headers[RetryPolicy::RETRY_COUNT_HEADER] = new_count
|
data/lib/legion/guardrails.rb
CHANGED
|
@@ -7,7 +7,10 @@ module Legion
|
|
|
7
7
|
module EmbeddingSimilarity
|
|
8
8
|
class << self
|
|
9
9
|
def check(input, safe_embeddings:, threshold: 0.3)
|
|
10
|
-
|
|
10
|
+
unless defined?(Legion::LLM) && Legion::LLM.respond_to?(:embed) && Legion::LLM.respond_to?(:started?) && Legion::LLM.started?
|
|
11
|
+
return { safe: true,
|
|
12
|
+
reason: 'no embeddings service' }
|
|
13
|
+
end
|
|
11
14
|
|
|
12
15
|
input_vec = Legion::LLM.embed(input)
|
|
13
16
|
return { safe: true, reason: 'embedding failed' } unless input_vec
|
|
@@ -18,6 +21,8 @@ module Legion
|
|
|
18
21
|
Legion::Logging.warn "[Guardrails] EmbeddingSimilarity rejected input: distance=#{min_dist.round(4)} threshold=#{threshold}"
|
|
19
22
|
end
|
|
20
23
|
{ safe: safe, distance: min_dist.round(4), threshold: threshold }
|
|
24
|
+
rescue StandardError
|
|
25
|
+
{ safe: true, reason: 'embedding failed' }
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
def cosine_distance(vec_a, vec_b)
|
data/lib/legion/service.rb
CHANGED
|
@@ -452,6 +452,7 @@ module Legion
|
|
|
452
452
|
log.info 'Setting up Legion::LLM'
|
|
453
453
|
require 'legion/llm'
|
|
454
454
|
Legion::Settings.merge_settings('llm', Legion::LLM::Settings.default)
|
|
455
|
+
Legion::Settings.loader.settings[:llm][:api][:use_namespaces] = true
|
|
455
456
|
preload_llm_providers
|
|
456
457
|
Legion::LLM.start
|
|
457
458
|
log.info 'Legion::LLM started'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
alter_table(:tool_embedding_cache) do
|
|
6
|
+
add_index :tool_name, name: :idx_tool_embedding_cache_tool_name
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
down do
|
|
11
|
+
alter_table(:tool_embedding_cache) do
|
|
12
|
+
drop_index :tool_name, name: :idx_tool_embedding_cache_tool_name
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/legion/trace_search.rb
CHANGED
|
@@ -72,6 +72,9 @@ module Legion
|
|
|
72
72
|
)
|
|
73
73
|
Legion::Logging.error "[TraceSearch] LLM filter generation failed for query: #{query.inspect}" if !result[:valid] && defined?(Legion::Logging)
|
|
74
74
|
result[:data] if result[:valid]
|
|
75
|
+
rescue Legion::LLM::LLMError => e
|
|
76
|
+
handle_exception(e, level: :debug, handled: true, operation: 'trace_search.generate_filter') if respond_to?(:handle_exception)
|
|
77
|
+
nil
|
|
75
78
|
end
|
|
76
79
|
|
|
77
80
|
def schema_context
|
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.9.
|
|
4
|
+
version: 1.9.38
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -351,14 +351,14 @@ dependencies:
|
|
|
351
351
|
requirements:
|
|
352
352
|
- - ">="
|
|
353
353
|
- !ruby/object:Gem::Version
|
|
354
|
-
version: 0.
|
|
354
|
+
version: 0.10.1
|
|
355
355
|
type: :runtime
|
|
356
356
|
prerelease: false
|
|
357
357
|
version_requirements: !ruby/object:Gem::Requirement
|
|
358
358
|
requirements:
|
|
359
359
|
- - ">="
|
|
360
360
|
- !ruby/object:Gem::Version
|
|
361
|
-
version: 0.
|
|
361
|
+
version: 0.10.1
|
|
362
362
|
- !ruby/object:Gem::Dependency
|
|
363
363
|
name: legion-tty
|
|
364
364
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -780,6 +780,7 @@ files:
|
|
|
780
780
|
- lib/legion/data/local_migrations/20250601000001_create_tbi_patterns.rb
|
|
781
781
|
- lib/legion/data/local_migrations/20260319000001_create_extension_catalog.rb
|
|
782
782
|
- lib/legion/data/local_migrations/20260319000002_create_extension_permissions.rb
|
|
783
|
+
- lib/legion/data/local_migrations/20260528000001_add_tbi_patterns_indexes.rb
|
|
783
784
|
- lib/legion/data/models/tbi_pattern.rb
|
|
784
785
|
- lib/legion/digital_worker.rb
|
|
785
786
|
- lib/legion/digital_worker/airb.rb
|
|
@@ -914,6 +915,7 @@ files:
|
|
|
914
915
|
- lib/legion/tools/do.rb
|
|
915
916
|
- lib/legion/tools/embedding_cache.rb
|
|
916
917
|
- lib/legion/tools/embedding_cache/migrations/001_create_tool_embedding_cache.rb
|
|
918
|
+
- lib/legion/tools/embedding_cache/migrations/002_add_tool_name_index.rb
|
|
917
919
|
- lib/legion/tools/registry.rb
|
|
918
920
|
- lib/legion/tools/status.rb
|
|
919
921
|
- lib/legion/tools/trigger_index.rb
|