legionio 1.5.15 → 1.5.19
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 +28 -0
- data/Gemfile +1 -0
- data/legionio.gemspec +1 -0
- data/lib/legion/cli/knowledge_command.rb +140 -0
- data/lib/legion/cli.rb +5 -1
- data/lib/legion/extensions/core.rb +13 -0
- data/lib/legion/extensions/helpers/knowledge.rb +141 -0
- data/lib/legion/extensions/helpers/llm.rb +18 -0
- data/lib/legion/readiness.rb +1 -1
- data/lib/legion/service.rb +21 -0
- data/lib/legion/version.rb +1 -1
- metadata +18 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bb76821fb4802e78a19c4518c18f32a961a144cd96f0ef146ce0b582ef96f699
|
|
4
|
+
data.tar.gz: c9c6eb50bef1df98a3ede6ce943e4a45a9dc4c638ce682519708d3d0008eddd5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2e502641a0f4177baeab32e78b839f80fa163953fe55770cd0a31364b352b5eda156e0971e67057ad5263160cc5f874b7cdbfdafc1a7cc9799fdd7a9ebd73a65
|
|
7
|
+
data.tar.gz: 90ec056445d835e01d044dd1ca6272c7e30ef5cffa0e30c39d93d4c2e8004f0e4d3ff59d83e12753bc485484a68a2526e90ebe1ccfdd1fb39e12b89e549d01d1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.19] - 2026-03-26
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `legion knowledge` CLI subcommand: query, retrieve, ingest, status (closes #36)
|
|
7
|
+
- `legion knowledge query QUESTION` — synthesized LLM answer + ranked source chunks
|
|
8
|
+
- `legion knowledge retrieve QUESTION` — raw source chunks without synthesis
|
|
9
|
+
- `legion knowledge ingest PATH` — ingest file or directory corpus
|
|
10
|
+
- `legion knowledge status` — show corpus file count and size
|
|
11
|
+
|
|
12
|
+
## [1.5.18] - 2026-03-25
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- `scope:` parameter on `Helpers::Knowledge` (`ingest_knowledge` and `query_knowledge`)
|
|
16
|
+
- Scope routing: `:local` -> `Apollo::Local`, `:global` -> `Apollo`, `:all` -> both with local-first dedup
|
|
17
|
+
- Default query scope configurable via `Settings[:apollo][:local][:default_query_scope]`
|
|
18
|
+
- `setup_apollo` now starts `Apollo::Local` when available
|
|
19
|
+
|
|
20
|
+
## [1.5.17] - 2026-03-25
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- `Helpers::Knowledge` — universal `ingest_knowledge` and `query_knowledge` mixin for all extensions; included automatically in `Extensions::Core`
|
|
24
|
+
- Automatic file extraction via `Legion::Data::Extract` when a file path is passed to `ingest_knowledge`
|
|
25
|
+
- Graceful degradation when `Legion::Apollo` or `Legion::Data::Extract` are not available
|
|
26
|
+
- `setup_apollo` in `Service` boot sequence (between LLM and GAIA); wires `Legion::Apollo.start` with `LoadError`/`StandardError` rescue
|
|
27
|
+
- `:apollo` added to `Readiness::COMPONENTS` between `:llm` and `:gaia`
|
|
28
|
+
- `legion-apollo >= 0.2.1` dependency in gemspec
|
|
29
|
+
- `Helpers::LLM#llm_embed` in LegionIO now forwards all keyword arguments (`provider:`, `dimensions:`, etc.) via anonymous `**` forwarding
|
|
30
|
+
|
|
3
31
|
## [1.5.15] - 2026-03-25
|
|
4
32
|
|
|
5
33
|
### Removed
|
data/Gemfile
CHANGED
|
@@ -11,6 +11,7 @@ gem 'legion-logging', path: '../legion-logging' if File.exist?(File.expand_path(
|
|
|
11
11
|
gem 'legion-mcp', path: '../legion-mcp' if File.exist?(File.expand_path('../legion-mcp', __dir__))
|
|
12
12
|
gem 'legion-settings', path: '../legion-settings' if File.exist?(File.expand_path('../legion-settings', __dir__))
|
|
13
13
|
|
|
14
|
+
gem 'legion-apollo', path: '../legion-apollo' if File.exist?(File.expand_path('../legion-apollo', __dir__))
|
|
14
15
|
gem 'lex-agentic-memory', path: '../extensions-agentic/lex-agentic-memory' if File.exist?(File.expand_path('../extensions-agentic/lex-agentic-memory', __dir__))
|
|
15
16
|
gem 'lex-llm-gateway', path: '../extensions-core/lex-llm-gateway' if File.exist?(File.expand_path('../extensions-core/lex-llm-gateway', __dir__))
|
|
16
17
|
gem 'lex-microsoft_teams', path: '../extensions/lex-microsoft_teams' if File.exist?(File.expand_path('../extensions/lex-microsoft_teams', __dir__))
|
data/legionio.gemspec
CHANGED
|
@@ -60,6 +60,7 @@ Gem::Specification.new do |spec|
|
|
|
60
60
|
spec.add_dependency 'legion-settings', '>= 1.3.19'
|
|
61
61
|
spec.add_dependency 'legion-transport', '>= 1.4.0'
|
|
62
62
|
|
|
63
|
+
spec.add_dependency 'legion-apollo', '>= 0.2.1'
|
|
63
64
|
spec.add_dependency 'legion-gaia', '>= 0.9.24'
|
|
64
65
|
spec.add_dependency 'legion-llm', '>= 0.5.8'
|
|
65
66
|
spec.add_dependency 'legion-tty', '>= 0.4.35'
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module CLI
|
|
5
|
+
class Knowledge < Thor
|
|
6
|
+
def self.exit_on_failure?
|
|
7
|
+
true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class_option :json, type: :boolean, default: false, desc: 'Output as JSON'
|
|
11
|
+
class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
|
|
12
|
+
|
|
13
|
+
desc 'query QUESTION', 'Query the knowledge base with optional LLM synthesis'
|
|
14
|
+
option :top_k, type: :numeric, default: 5, desc: 'Number of source chunks'
|
|
15
|
+
option :synthesize, type: :boolean, default: true, desc: 'Synthesize an LLM answer'
|
|
16
|
+
option :verbose, type: :boolean, default: false, desc: 'Show full source metadata'
|
|
17
|
+
def query(question)
|
|
18
|
+
require_knowledge!
|
|
19
|
+
result = knowledge_query.query(question: question, top_k: options[:top_k],
|
|
20
|
+
synthesize: options[:synthesize])
|
|
21
|
+
out = formatter
|
|
22
|
+
if options[:json]
|
|
23
|
+
out.json(result)
|
|
24
|
+
elsif result[:success]
|
|
25
|
+
out.header('Knowledge Query')
|
|
26
|
+
if result[:answer]
|
|
27
|
+
out.spacer
|
|
28
|
+
puts result[:answer]
|
|
29
|
+
out.spacer
|
|
30
|
+
end
|
|
31
|
+
print_sources(result[:sources] || [], out, verbose: options[:verbose])
|
|
32
|
+
else
|
|
33
|
+
out.warn("Query failed: #{result[:error]}")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
default_task :help
|
|
37
|
+
|
|
38
|
+
desc 'retrieve QUESTION', 'Retrieve source chunks without LLM synthesis'
|
|
39
|
+
option :top_k, type: :numeric, default: 5, desc: 'Number of source chunks'
|
|
40
|
+
def retrieve(question)
|
|
41
|
+
require_knowledge!
|
|
42
|
+
result = knowledge_query.retrieve(question: question, top_k: options[:top_k])
|
|
43
|
+
out = formatter
|
|
44
|
+
if options[:json]
|
|
45
|
+
out.json(result)
|
|
46
|
+
elsif result[:success]
|
|
47
|
+
out.header("Knowledge Retrieve (#{(result[:sources] || []).size} chunks)")
|
|
48
|
+
print_sources(result[:sources] || [], out, verbose: true)
|
|
49
|
+
else
|
|
50
|
+
out.warn("Retrieve failed: #{result[:error]}")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
desc 'ingest PATH', 'Ingest a file or directory into the knowledge base'
|
|
55
|
+
option :force, type: :boolean, default: false, desc: 'Re-ingest even unchanged files'
|
|
56
|
+
option :dry_run, type: :boolean, default: false, desc: 'Preview without writing'
|
|
57
|
+
def ingest(path)
|
|
58
|
+
require_ingest!
|
|
59
|
+
result = if ::File.directory?(path)
|
|
60
|
+
knowledge_ingest.ingest_corpus(path: path, force: options[:force],
|
|
61
|
+
dry_run: options[:dry_run])
|
|
62
|
+
else
|
|
63
|
+
knowledge_ingest.ingest_file(file_path: path, force: options[:force],
|
|
64
|
+
dry_run: options[:dry_run])
|
|
65
|
+
end
|
|
66
|
+
out = formatter
|
|
67
|
+
if options[:json]
|
|
68
|
+
out.json(result)
|
|
69
|
+
elsif result[:success]
|
|
70
|
+
out.success('Ingest complete')
|
|
71
|
+
out.detail(result.except(:success))
|
|
72
|
+
else
|
|
73
|
+
out.warn("Ingest failed: #{result[:error]}")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
desc 'status', 'Show knowledge base status'
|
|
78
|
+
def status
|
|
79
|
+
require_ingest!
|
|
80
|
+
result = knowledge_ingest.scan_corpus(path: ::Dir.pwd)
|
|
81
|
+
out = formatter
|
|
82
|
+
if options[:json]
|
|
83
|
+
out.json(result)
|
|
84
|
+
else
|
|
85
|
+
out.header('Knowledge Status')
|
|
86
|
+
out.detail({
|
|
87
|
+
'Path' => result[:path].to_s,
|
|
88
|
+
'Files' => result[:file_count].to_s,
|
|
89
|
+
'Total size' => "#{result[:total_bytes]} bytes"
|
|
90
|
+
})
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
no_commands do
|
|
95
|
+
def formatter
|
|
96
|
+
@formatter ||= Output::Formatter.new(json: options[:json], color: !options[:no_color])
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def require_knowledge!
|
|
100
|
+
return if defined?(Legion::Extensions::Knowledge::Runners::Query)
|
|
101
|
+
|
|
102
|
+
raise CLI::Error, 'lex-knowledge extension is not loaded. Install and enable it first.'
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def require_ingest!
|
|
106
|
+
return if defined?(Legion::Extensions::Knowledge::Runners::Ingest)
|
|
107
|
+
|
|
108
|
+
raise CLI::Error, 'lex-knowledge extension is not loaded. Install and enable it first.'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def knowledge_query
|
|
112
|
+
Legion::Extensions::Knowledge::Runners::Query
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def knowledge_ingest
|
|
116
|
+
Legion::Extensions::Knowledge::Runners::Ingest
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def print_sources(sources, out, verbose:)
|
|
120
|
+
return out.warn('No sources found') if sources.empty?
|
|
121
|
+
|
|
122
|
+
out.header("Sources (#{sources.size})")
|
|
123
|
+
sources.each_with_index do |s, i|
|
|
124
|
+
score = format('%.2f', s[:score].to_f)
|
|
125
|
+
heading = s[:heading].to_s.empty? ? '' : " \u00a7 #{s[:heading]}"
|
|
126
|
+
puts " #{i + 1}. #{s[:source_file]}#{heading} score: #{score}"
|
|
127
|
+
puts " #{truncate(s[:content].to_s, 100)}" if verbose
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def truncate(text, max)
|
|
132
|
+
return text if text.length <= max
|
|
133
|
+
return text[0, max] if max < 4
|
|
134
|
+
|
|
135
|
+
"#{text[0, max - 3]}..."
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
data/lib/legion/cli.rb
CHANGED
|
@@ -42,6 +42,7 @@ module Legion
|
|
|
42
42
|
autoload :Eval, 'legion/cli/eval_command'
|
|
43
43
|
autoload :Update, 'legion/cli/update_command'
|
|
44
44
|
autoload :Init, 'legion/cli/init_command'
|
|
45
|
+
autoload :Knowledge, 'legion/cli/knowledge_command'
|
|
45
46
|
autoload :Setup, 'legion/cli/setup_command'
|
|
46
47
|
autoload :Skill, 'legion/cli/skill_command'
|
|
47
48
|
autoload :Prompt, 'legion/cli/prompt_command'
|
|
@@ -256,6 +257,9 @@ module Legion
|
|
|
256
257
|
desc 'apollo SUBCOMMAND', 'Apollo knowledge graph'
|
|
257
258
|
subcommand 'apollo', Legion::CLI::Apollo
|
|
258
259
|
|
|
260
|
+
desc 'knowledge SUBCOMMAND', 'Search and manage the document knowledge base'
|
|
261
|
+
subcommand 'knowledge', Legion::CLI::Knowledge
|
|
262
|
+
|
|
259
263
|
desc 'schedule SUBCOMMAND', 'Manage schedules'
|
|
260
264
|
subcommand 'schedule', Legion::CLI::Schedule
|
|
261
265
|
|
|
@@ -348,7 +352,7 @@ module Legion
|
|
|
348
352
|
|
|
349
353
|
desc 'tree', 'Print a tree of all available commands'
|
|
350
354
|
def tree
|
|
351
|
-
legion_print_command_tree(self.class,
|
|
355
|
+
legion_print_command_tree(self.class, ::File.basename($PROGRAM_NAME), '')
|
|
352
356
|
end
|
|
353
357
|
|
|
354
358
|
desc 'ask TEXT', 'Quick AI prompt (shortcut for chat prompt)'
|
|
@@ -21,6 +21,18 @@ rescue LoadError => e
|
|
|
21
21
|
Legion::Logging.debug "Extensions::Core: legion-llm helpers not available: #{e.message}" if defined?(Legion::Logging)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
begin
|
|
25
|
+
require_relative 'helpers/llm'
|
|
26
|
+
rescue LoadError => e
|
|
27
|
+
Legion::Logging.debug "Extensions::Core: local llm helper not available: #{e.message}" if defined?(Legion::Logging)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
require_relative 'helpers/knowledge'
|
|
32
|
+
rescue LoadError => e
|
|
33
|
+
Legion::Logging.debug "Extensions::Core: knowledge helper not available: #{e.message}" if defined?(Legion::Logging)
|
|
34
|
+
end
|
|
35
|
+
|
|
24
36
|
require_relative 'actors/base'
|
|
25
37
|
require_relative 'actors/every'
|
|
26
38
|
require_relative 'actors/loop'
|
|
@@ -35,6 +47,7 @@ module Legion
|
|
|
35
47
|
module Core
|
|
36
48
|
include Legion::Extensions::Helpers::Transport
|
|
37
49
|
include Legion::Extensions::Helpers::Lex
|
|
50
|
+
include Legion::Extensions::Helpers::Knowledge if defined?(Legion::Extensions::Helpers::Knowledge)
|
|
38
51
|
|
|
39
52
|
include Legion::Extensions::Builder::Runners
|
|
40
53
|
include Legion::Extensions::Builder::Helpers
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Helpers
|
|
6
|
+
module Knowledge
|
|
7
|
+
def ingest_knowledge(content_or_path, type: :auto, tags: [], scope: :global, **opts)
|
|
8
|
+
target = resolve_ingest_target(scope)
|
|
9
|
+
return { success: false, error: :apollo_not_available } unless target
|
|
10
|
+
|
|
11
|
+
text, metadata = extract_if_needed(content_or_path, type: type)
|
|
12
|
+
return { success: false, error: :extraction_failed, detail: metadata } unless text
|
|
13
|
+
|
|
14
|
+
extraction_tags = metadata_to_tags(metadata) if metadata
|
|
15
|
+
all_tags = Array(tags) + Array(extraction_tags)
|
|
16
|
+
|
|
17
|
+
target.ingest(
|
|
18
|
+
content: text,
|
|
19
|
+
tags: all_tags,
|
|
20
|
+
source_channel: opts[:source_channel] || derive_lex_name,
|
|
21
|
+
**opts.except(:source_channel)
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def query_knowledge(text:, limit: 5, scope: nil, **)
|
|
26
|
+
scope ||= default_query_scope
|
|
27
|
+
|
|
28
|
+
case scope.to_sym
|
|
29
|
+
when :local then query_local(text: text, limit: limit, **)
|
|
30
|
+
when :global then query_global(text: text, limit: limit, **)
|
|
31
|
+
else query_all(text: text, limit: limit, **)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def resolve_ingest_target(scope)
|
|
38
|
+
case scope.to_sym
|
|
39
|
+
when :local
|
|
40
|
+
local_available? ? Legion::Apollo::Local : nil
|
|
41
|
+
else
|
|
42
|
+
global_available? ? Legion::Apollo : nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def query_local(text:, limit:, **)
|
|
47
|
+
unless local_available?
|
|
48
|
+
Legion::Logging.debug 'query_knowledge(:local) called but Apollo::Local is not available' if defined?(Legion::Logging)
|
|
49
|
+
return { success: false, error: :apollo_not_available }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
Legion::Apollo::Local.query(text: text, limit: limit, **)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def query_global(text:, limit:, **)
|
|
56
|
+
unless global_available?
|
|
57
|
+
Legion::Logging.debug 'query_knowledge(:global) called but Apollo is not available' if defined?(Legion::Logging)
|
|
58
|
+
return { success: false, error: :apollo_not_available }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Legion::Apollo.query(text: text, limit: limit, **)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def query_all(text:, limit:, **)
|
|
65
|
+
local_results = local_available? ? Array((Legion::Apollo::Local.query(text: text, limit: limit, **) || {})[:results]) : []
|
|
66
|
+
global_results = global_available? ? Array((Legion::Apollo.query(text: text, limit: limit, **) || {})[:results]) : []
|
|
67
|
+
|
|
68
|
+
return { success: false, error: :apollo_not_available } if local_results.empty? && global_results.empty? && !local_available? && !global_available?
|
|
69
|
+
|
|
70
|
+
merged = merge_results(local_results, global_results)
|
|
71
|
+
{ success: true, results: merged.first(limit), count: [merged.size, limit].min, mode: :all }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def merge_results(local_results, global_results)
|
|
75
|
+
seen = {}
|
|
76
|
+
merged = []
|
|
77
|
+
|
|
78
|
+
local_results.each do |r|
|
|
79
|
+
key = r[:content_hash] || r[:content]
|
|
80
|
+
seen[key] = true
|
|
81
|
+
merged << r
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
global_results.each do |r|
|
|
85
|
+
key = r[:content_hash] || r[:content]
|
|
86
|
+
merged << r unless seen[key]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
merged
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def global_available?
|
|
93
|
+
defined?(Legion::Apollo) && Legion::Apollo.started?
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def local_available?
|
|
97
|
+
defined?(Legion::Apollo::Local) && Legion::Apollo::Local.started?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def default_query_scope
|
|
101
|
+
return :all unless defined?(Legion::Settings)
|
|
102
|
+
|
|
103
|
+
scope = Legion::Settings.dig(:apollo, :local, :default_query_scope)
|
|
104
|
+
scope ? scope.to_sym : :all
|
|
105
|
+
rescue StandardError
|
|
106
|
+
:all
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def extract_if_needed(content_or_path, type:)
|
|
110
|
+
return extract_file(content_or_path, type: type) if content_or_path.is_a?(String) && File.exist?(content_or_path)
|
|
111
|
+
return extract_file(content_or_path, type: type) if content_or_path.respond_to?(:read)
|
|
112
|
+
|
|
113
|
+
[content_or_path.to_s, nil]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def extract_file(source, type:)
|
|
117
|
+
return [source.to_s, nil] unless defined?(Legion::Data::Extract)
|
|
118
|
+
|
|
119
|
+
result = Legion::Data::Extract.extract(source, type: type)
|
|
120
|
+
if result[:text]
|
|
121
|
+
[result[:text], result[:metadata]]
|
|
122
|
+
else
|
|
123
|
+
[nil, result]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def metadata_to_tags(metadata)
|
|
128
|
+
tags = []
|
|
129
|
+
tags << metadata[:type].to_s if metadata[:type]
|
|
130
|
+
tags << "pages:#{metadata[:pages]}" if metadata[:pages]
|
|
131
|
+
tags
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def derive_lex_name
|
|
135
|
+
parts = self.class.name&.split('::')
|
|
136
|
+
parts && parts[2] ? parts[2].downcase : 'unknown'
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Helpers
|
|
6
|
+
module LLM
|
|
7
|
+
# Quick embed from any extension runner, forwarding all keyword arguments.
|
|
8
|
+
# Supports provider:, dimensions:, and any future parameters.
|
|
9
|
+
# @param text [String, Array<String>] text to embed
|
|
10
|
+
# @param kwargs [Hash] forwarded to Legion::LLM.embed (model:, provider:, dimensions:, etc.)
|
|
11
|
+
# @return [Hash] embedding result with :vector, :dimensions, :model, :provider
|
|
12
|
+
def llm_embed(text, **)
|
|
13
|
+
Legion::LLM.embed(text, **)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/legion/readiness.rb
CHANGED
data/lib/legion/service.rb
CHANGED
|
@@ -98,6 +98,15 @@ module Legion
|
|
|
98
98
|
end
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
begin
|
|
102
|
+
setup_apollo
|
|
103
|
+
Legion::Readiness.mark_ready(:apollo)
|
|
104
|
+
rescue LoadError
|
|
105
|
+
Legion::Logging.info 'Legion::Apollo gem is not installed, starting without Apollo'
|
|
106
|
+
rescue StandardError => e
|
|
107
|
+
Legion::Logging.warn "Legion::Apollo failed to load: #{e.message}"
|
|
108
|
+
end
|
|
109
|
+
|
|
101
110
|
if gaia
|
|
102
111
|
begin
|
|
103
112
|
setup_gaia
|
|
@@ -317,6 +326,18 @@ module Legion
|
|
|
317
326
|
Legion::Logging.warn "Legion::Gaia failed to load: #{e.message}"
|
|
318
327
|
end
|
|
319
328
|
|
|
329
|
+
def setup_apollo
|
|
330
|
+
Legion::Logging.info 'Setting up Legion::Apollo'
|
|
331
|
+
require 'legion/apollo'
|
|
332
|
+
Legion::Apollo.start
|
|
333
|
+
Legion::Apollo::Local.start if defined?(Legion::Apollo::Local)
|
|
334
|
+
Legion::Logging.info 'Legion::Apollo started'
|
|
335
|
+
rescue LoadError
|
|
336
|
+
Legion::Logging.info 'Legion::Apollo gem is not installed, starting without Apollo'
|
|
337
|
+
rescue StandardError => e
|
|
338
|
+
Legion::Logging.warn "Legion::Apollo failed to load: #{e.message}"
|
|
339
|
+
end
|
|
340
|
+
|
|
320
341
|
def setup_transport
|
|
321
342
|
Legion::Logging.info 'Setting up Legion::Transport'
|
|
322
343
|
require 'legion/transport'
|
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.5.
|
|
4
|
+
version: 1.5.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -317,6 +317,20 @@ dependencies:
|
|
|
317
317
|
- - ">="
|
|
318
318
|
- !ruby/object:Gem::Version
|
|
319
319
|
version: 1.4.0
|
|
320
|
+
- !ruby/object:Gem::Dependency
|
|
321
|
+
name: legion-apollo
|
|
322
|
+
requirement: !ruby/object:Gem::Requirement
|
|
323
|
+
requirements:
|
|
324
|
+
- - ">="
|
|
325
|
+
- !ruby/object:Gem::Version
|
|
326
|
+
version: 0.2.1
|
|
327
|
+
type: :runtime
|
|
328
|
+
prerelease: false
|
|
329
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
330
|
+
requirements:
|
|
331
|
+
- - ">="
|
|
332
|
+
- !ruby/object:Gem::Version
|
|
333
|
+
version: 0.2.1
|
|
320
334
|
- !ruby/object:Gem::Dependency
|
|
321
335
|
name: legion-gaia
|
|
322
336
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -616,6 +630,7 @@ files:
|
|
|
616
630
|
- lib/legion/cli/init/environment_detector.rb
|
|
617
631
|
- lib/legion/cli/init_command.rb
|
|
618
632
|
- lib/legion/cli/interactive.rb
|
|
633
|
+
- lib/legion/cli/knowledge_command.rb
|
|
619
634
|
- lib/legion/cli/lex/actor.rb
|
|
620
635
|
- lib/legion/cli/lex/exchange.rb
|
|
621
636
|
- lib/legion/cli/lex/message.rb
|
|
@@ -746,7 +761,9 @@ files:
|
|
|
746
761
|
- lib/legion/extensions/helpers/cache.rb
|
|
747
762
|
- lib/legion/extensions/helpers/core.rb
|
|
748
763
|
- lib/legion/extensions/helpers/data.rb
|
|
764
|
+
- lib/legion/extensions/helpers/knowledge.rb
|
|
749
765
|
- lib/legion/extensions/helpers/lex.rb
|
|
766
|
+
- lib/legion/extensions/helpers/llm.rb
|
|
750
767
|
- lib/legion/extensions/helpers/logger.rb
|
|
751
768
|
- lib/legion/extensions/helpers/segments.rb
|
|
752
769
|
- lib/legion/extensions/helpers/task.rb
|