legion-llm 0.9.29 → 0.9.32
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 +23 -0
- data/lib/legion/llm/api/anthropic/messages.rb +1 -1
- data/lib/legion/llm/api/native/inference.rb +5 -1
- data/lib/legion/llm/api/openai/chat_completions.rb +1 -1
- data/lib/legion/llm/hooks/reflection.rb +3 -3
- data/lib/legion/llm/inference/executor.rb +83 -26
- data/lib/legion/llm/inference/steps/rag_context.rb +25 -2
- data/lib/legion/llm/settings.rb +7 -5
- data/lib/legion/llm/tools/special.rb +46 -1
- data/lib/legion/llm/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 715bd8c0918939545eda0cf832d81aa23e69c807ed6055fdb4d95f4177c99449
|
|
4
|
+
data.tar.gz: 12e42d3d2fdc02c4ca7764a264af90631141952982d9726c02d0e9ea7de87a92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6f2fd1a0ea8b18ed222f2713adb4f4a48ce57e90d5a3ac2242f7ae648ed297b6375d1143c0e29777e4c5ade60e479e09997c2af04cf0e1d3b81225ef3a14276f
|
|
7
|
+
data.tar.gz: 41d0daa21a98518c4192881bd3231ca1e308b6231d06e22b6102531feee4f6aef1ced50d9e43b1f65b7e8f344e95406ebb682bf445d356011aafd6d4ce241a37
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Legion LLM Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.31] - 2026-05-18
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Tools: `legion_list_all_tools` pinned special tool for full extension tool discovery with extension/deferred filters
|
|
7
|
+
- RAG: debug logging for per-result confidence scores and content_type distribution
|
|
8
|
+
- RAG: `exclude_source_agents` setting to filter noisy bulk-ingest sources from context injection
|
|
9
|
+
- Settings: `client_tool_passthrough` configurable via `Legion::Settings[:llm][:tool_trigger][:client_tool_passthrough]`
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- RAG: `min_confidence` default raised from 0.5 to 0.85 (reduces irrelevant context)
|
|
13
|
+
- RAG: default exclusion list: teams-api-ingest, unknown, teams-entity-extractor, legion-interlink
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Executor: enrichment injection (system prompt + RAG) cached on first tool loop pass; subsequent rounds reuse cache
|
|
17
|
+
- Client tool passthrough: per-request explicit false now correctly disables (was broken by || on false values)
|
|
18
|
+
- Tool audit publishing deferred to post-metering flush (async, non-blocking)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## [0.9.30] - 2026-05-16
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- Tools: API-submitted client tools are no longer advertised to providers unless callers explicitly opt into client-tool passthrough, preventing incomplete tool calls from ending streaming responses.
|
|
25
|
+
|
|
3
26
|
## [0.9.29] - 2026-05-16
|
|
4
27
|
|
|
5
28
|
### Added
|
|
@@ -149,7 +149,7 @@ module Legion
|
|
|
149
149
|
name: tname,
|
|
150
150
|
description: tdesc,
|
|
151
151
|
parameters: tschema,
|
|
152
|
-
source: { type: :client, executable:
|
|
152
|
+
source: { type: :client, executable: true }
|
|
153
153
|
)
|
|
154
154
|
rescue StandardError => e
|
|
155
155
|
log.warn("[llm][api][anthropic][messages] build_tool_classes failed name=#{tname} error=#{e.message}")
|
|
@@ -28,6 +28,7 @@ module Legion
|
|
|
28
28
|
conversation_id = body[:conversation_id]
|
|
29
29
|
request_id = body[:request_id] || SecureRandom.uuid
|
|
30
30
|
include_thinking = body[:include_thinking] == true
|
|
31
|
+
client_tool_passthrough = body[:client_tool_passthrough] == true
|
|
31
32
|
|
|
32
33
|
unless messages.is_a?(Array)
|
|
33
34
|
halt 400, { 'Content-Type' => 'application/json' },
|
|
@@ -103,6 +104,9 @@ module Legion
|
|
|
103
104
|
|
|
104
105
|
extra = {}
|
|
105
106
|
extra[:tier] = tier.to_sym if tier
|
|
107
|
+
metadata = { requested_tools: requested_tools }
|
|
108
|
+
metadata[:client_tool_passthrough] = true if client_tool_passthrough
|
|
109
|
+
metadata[:client_tool_request_count] = tools.size if tools.any?
|
|
106
110
|
|
|
107
111
|
pipeline_request = Legion::LLM::Inference::Request.build(
|
|
108
112
|
id: request_id,
|
|
@@ -112,7 +116,7 @@ module Legion
|
|
|
112
116
|
tools: tool_declarations,
|
|
113
117
|
caller: effective_caller,
|
|
114
118
|
conversation_id: conversation_id,
|
|
115
|
-
metadata:
|
|
119
|
+
metadata: metadata,
|
|
116
120
|
stream: streaming,
|
|
117
121
|
cache: { strategy: :default, cacheable: true },
|
|
118
122
|
extra: extra
|
|
@@ -123,7 +123,7 @@ module Legion
|
|
|
123
123
|
name: t[:name].to_s,
|
|
124
124
|
description: t[:description].to_s,
|
|
125
125
|
parameters: t[:parameters] || {},
|
|
126
|
-
source: { type: :client, executable:
|
|
126
|
+
source: { type: :client, executable: true }
|
|
127
127
|
)
|
|
128
128
|
rescue StandardError => e
|
|
129
129
|
tool_name = t.is_a?(Hash) ? t[:name] : nil
|
|
@@ -176,13 +176,13 @@ module Legion
|
|
|
176
176
|
)
|
|
177
177
|
log.info("[llm][reflection] published via=transport model=#{model} type=#{entry[:type]}")
|
|
178
178
|
elsif apollo_direct?
|
|
179
|
-
Legion::Extensions::Apollo::Runners::
|
|
179
|
+
Legion::Extensions::Apollo::Runners::Request.ingest(
|
|
180
180
|
content: entry[:content],
|
|
181
181
|
content_type: entry[:type].to_s,
|
|
182
182
|
knowledge_domain: 'reflection',
|
|
183
183
|
confidence: entry[:confidence],
|
|
184
184
|
source_agent: "llm:#{model}",
|
|
185
|
-
|
|
185
|
+
source_channel: 'reflection_hook'
|
|
186
186
|
)
|
|
187
187
|
log.info("[llm][reflection] published via=direct model=#{model} type=#{entry[:type]}")
|
|
188
188
|
end
|
|
@@ -236,7 +236,7 @@ module Legion
|
|
|
236
236
|
private_class_method :apollo_transport?
|
|
237
237
|
|
|
238
238
|
def apollo_direct?
|
|
239
|
-
defined?(Legion::Extensions::Apollo::Runners::
|
|
239
|
+
defined?(Legion::Extensions::Apollo::Runners::Request)
|
|
240
240
|
end
|
|
241
241
|
private_class_method :apollo_direct?
|
|
242
242
|
end
|
|
@@ -102,6 +102,7 @@ module Legion
|
|
|
102
102
|
@sticky_turn_snapshot = nil
|
|
103
103
|
@pending_tool_history = Concurrent::Array.new
|
|
104
104
|
@pending_tool_history_mutex = Mutex.new
|
|
105
|
+
@deferred_tool_audits = []
|
|
105
106
|
@injected_tool_map = {}
|
|
106
107
|
@native_tool_source_map = {}
|
|
107
108
|
@freshly_triggered_keys = []
|
|
@@ -889,10 +890,14 @@ module Legion
|
|
|
889
890
|
end
|
|
890
891
|
|
|
891
892
|
def native_dispatch_options
|
|
892
|
-
injected_system =
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
893
|
+
injected_system = if @native_tool_loop_round.to_i.positive?
|
|
894
|
+
@cached_injected_system
|
|
895
|
+
else
|
|
896
|
+
@cached_injected_system = EnrichmentInjector.inject(
|
|
897
|
+
system: @request.system,
|
|
898
|
+
enrichments: @enrichments
|
|
899
|
+
)
|
|
900
|
+
end
|
|
896
901
|
|
|
897
902
|
options = {
|
|
898
903
|
system: injected_system,
|
|
@@ -939,6 +944,25 @@ module Legion
|
|
|
939
944
|
!(@request.respond_to?(:suppress_tools) && @request.suppress_tools)
|
|
940
945
|
end
|
|
941
946
|
|
|
947
|
+
def client_tool_passthrough_enabled?
|
|
948
|
+
if @request.respond_to?(:metadata)
|
|
949
|
+
metadata = @request.metadata || {}
|
|
950
|
+
value = metadata.key?(:client_tool_passthrough) ? metadata[:client_tool_passthrough] : metadata['client_tool_passthrough']
|
|
951
|
+
return value if [true, false].include?(value)
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
Legion::LLM::Settings.value(:tool_trigger, :client_tool_passthrough) != false
|
|
955
|
+
end
|
|
956
|
+
|
|
957
|
+
def non_executable_client_tool?(definition)
|
|
958
|
+
source = definition.respond_to?(:source) ? definition.source : {}
|
|
959
|
+
return false unless source.is_a?(Hash)
|
|
960
|
+
|
|
961
|
+
source_type = source[:type] || source['type']
|
|
962
|
+
executable = source.key?(:executable) ? source[:executable] : source['executable']
|
|
963
|
+
source_type.respond_to?(:to_sym) && source_type.to_sym == :client && executable != true
|
|
964
|
+
end
|
|
965
|
+
|
|
942
966
|
def add_pinned_special_tool_definitions(definitions)
|
|
943
967
|
Tools::Special.pinned_definitions.each do |definition|
|
|
944
968
|
next if definitions.any? { |existing| existing.name == definition.name }
|
|
@@ -957,6 +981,14 @@ module Legion
|
|
|
957
981
|
else
|
|
958
982
|
Types::ToolDefinition.from_tool_class(tool)
|
|
959
983
|
end
|
|
984
|
+
if non_executable_client_tool?(definition) && !client_tool_passthrough_enabled?
|
|
985
|
+
log.info(
|
|
986
|
+
"[llm][tools][inject] action=client_tool_skipped request_id=#{request_log_value(:id, 'unknown')} " \
|
|
987
|
+
"conversation_id=#{request_log_value(:conversation_id, 'none') || 'none'} name=#{definition.name} " \
|
|
988
|
+
'reason=client_passthrough_not_enabled'
|
|
989
|
+
)
|
|
990
|
+
return
|
|
991
|
+
end
|
|
960
992
|
return if gaia_tool_suppressed?(definition.name)
|
|
961
993
|
return if definitions.any? { |existing| existing.name == definition.name }
|
|
962
994
|
|
|
@@ -1401,28 +1433,52 @@ module Legion
|
|
|
1401
1433
|
end
|
|
1402
1434
|
|
|
1403
1435
|
def publish_tool_audit(tc_id, tc_name, result_str, is_error, duration_ms, started_at, finished_at)
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1436
|
+
@deferred_tool_audits << {
|
|
1437
|
+
tc_id: tc_id, tc_name: tc_name, result_str: result_str,
|
|
1438
|
+
is_error: is_error, duration_ms: duration_ms,
|
|
1439
|
+
started_at: started_at, finished_at: finished_at
|
|
1440
|
+
}
|
|
1441
|
+
end
|
|
1442
|
+
|
|
1443
|
+
def flush_deferred_tool_audits
|
|
1444
|
+
return if @deferred_tool_audits.empty?
|
|
1445
|
+
|
|
1446
|
+
audits = @deferred_tool_audits.dup
|
|
1447
|
+
@deferred_tool_audits.clear
|
|
1448
|
+
|
|
1449
|
+
request_id = @request.id
|
|
1450
|
+
conversation_id = @request.conversation_id
|
|
1451
|
+
exchange_id = @exchange_id
|
|
1452
|
+
caller_data = @request.caller
|
|
1453
|
+
classification = @request.classification
|
|
1454
|
+
tracing = @tracing
|
|
1455
|
+
|
|
1456
|
+
Concurrent::Promises.future do
|
|
1457
|
+
audits.each do |audit|
|
|
1458
|
+
Legion::LLM::Audit.emit_tools(
|
|
1459
|
+
request_id: request_id,
|
|
1460
|
+
conversation_id: conversation_id,
|
|
1461
|
+
exchange_id: exchange_id,
|
|
1462
|
+
tool_name: audit[:tc_name],
|
|
1463
|
+
tool_call: {
|
|
1464
|
+
id: audit[:tc_id],
|
|
1465
|
+
name: audit[:tc_name],
|
|
1466
|
+
status: audit[:is_error] ? :error : :success,
|
|
1467
|
+
duration_ms: audit[:duration_ms],
|
|
1468
|
+
started_at: audit[:started_at],
|
|
1469
|
+
finished_at: audit[:finished_at]
|
|
1470
|
+
},
|
|
1471
|
+
result: audit[:result_str][0, 4096],
|
|
1472
|
+
caller: caller_data,
|
|
1473
|
+
classification: classification,
|
|
1474
|
+
tracing: tracing,
|
|
1475
|
+
timestamp: audit[:finished_at],
|
|
1476
|
+
request_type: 'tool'
|
|
1477
|
+
)
|
|
1478
|
+
rescue StandardError => e
|
|
1479
|
+
Legion::Logging.log.warn("[llm][pipeline] publish_tool_audit failed tool=#{audit[:tc_name]}: #{e.message}")
|
|
1480
|
+
end
|
|
1481
|
+
end
|
|
1426
1482
|
end
|
|
1427
1483
|
|
|
1428
1484
|
def tool_call_field(tool_call, field)
|
|
@@ -1617,6 +1673,7 @@ module Legion
|
|
|
1617
1673
|
routing_reason: @audit.dig(:'routing:provider_selection', :data, :reason)
|
|
1618
1674
|
)
|
|
1619
1675
|
Steps::Metering.publish_or_spool(event)
|
|
1676
|
+
flush_deferred_tool_audits
|
|
1620
1677
|
rescue StandardError => e
|
|
1621
1678
|
@warnings << "metering error: #{e.message}"
|
|
1622
1679
|
handle_exception(e, level: :warn, operation: 'llm.pipeline.step_metering')
|
|
@@ -89,6 +89,13 @@ module Legion
|
|
|
89
89
|
return
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
scores = entries.filter_map { |e| e[:confidence] || e[:distance] }.map { |s| s.is_a?(Numeric) ? s.round(3) : s }
|
|
93
|
+
log.debug(
|
|
94
|
+
"[llm][steps][rag_context] action=results_scored request_id=#{@request&.id || 'unknown'} " \
|
|
95
|
+
"strategy=#{strategy} count=#{entries.size} scores=#{scores.inspect} " \
|
|
96
|
+
"types=#{entries.map { |e| e[:content_type] }.compact.tally.inspect}"
|
|
97
|
+
)
|
|
98
|
+
|
|
92
99
|
@enrichments['rag:context_retrieval'] = {
|
|
93
100
|
content: "#{Legion::LLM::Settings.config_value(result, :count)} entries retrieved via #{strategy}",
|
|
94
101
|
data: { entries: entries, strategy: strategy, count: Legion::LLM::Settings.config_value(result, :count) },
|
|
@@ -160,14 +167,30 @@ module Legion
|
|
|
160
167
|
def apollo_retrieve(query:, strategy:)
|
|
161
168
|
full_limit = rag_setting(:full_limit, 10)
|
|
162
169
|
compact_limit = rag_setting(:compact_limit, 5)
|
|
163
|
-
confidence = rag_setting(:min_confidence, 0.
|
|
170
|
+
confidence = rag_setting(:min_confidence, 0.85)
|
|
164
171
|
limit = apply_gaia_context_limit(strategy == :rag_compact ? compact_limit : full_limit,
|
|
165
172
|
strategy: strategy)
|
|
166
173
|
log_step_debug(:rag_context, :apollo_query, strategy: strategy, limit: limit, min_confidence: confidence)
|
|
167
174
|
|
|
168
175
|
general = apollo_retrieve_general(query: query, limit: limit, confidence: confidence)
|
|
169
176
|
history = apollo_retrieve_conversation_history(query: query, limit: limit, confidence: confidence)
|
|
170
|
-
merge_apollo_results(general, history)
|
|
177
|
+
result = merge_apollo_results(general, history)
|
|
178
|
+
filter_excluded_source_agents(result)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def filter_excluded_source_agents(result)
|
|
182
|
+
excluded = Array(rag_setting(:exclude_source_agents, []))
|
|
183
|
+
return result if excluded.empty?
|
|
184
|
+
|
|
185
|
+
entries = Array(result[:entries])
|
|
186
|
+
filtered = entries.reject { |e| excluded.include?(e[:source_agent].to_s) }
|
|
187
|
+
if filtered.size < entries.size
|
|
188
|
+
log.debug(
|
|
189
|
+
"[llm][steps][rag_context] action=source_agent_filter excluded=#{entries.size - filtered.size} " \
|
|
190
|
+
"remaining=#{filtered.size} agents=#{excluded.inspect}"
|
|
191
|
+
)
|
|
192
|
+
end
|
|
193
|
+
result.merge(entries: filtered, count: filtered.size)
|
|
171
194
|
end
|
|
172
195
|
|
|
173
196
|
def apollo_retrieve_general(query:, limit:, confidence:)
|
data/lib/legion/llm/settings.rb
CHANGED
|
@@ -377,12 +377,13 @@ module Legion
|
|
|
377
377
|
enabled: true,
|
|
378
378
|
full_limit: 10,
|
|
379
379
|
compact_limit: 5,
|
|
380
|
-
min_confidence: 0.
|
|
380
|
+
min_confidence: 0.85,
|
|
381
381
|
utilization_compact_threshold: 0.7,
|
|
382
382
|
utilization_skip_threshold: 0.9,
|
|
383
383
|
conversation_history_enabled: false,
|
|
384
384
|
trivial_max_chars: 20,
|
|
385
|
-
trivial_patterns: %w[hello hi hey ping pong test ok okay yes no thanks thank]
|
|
385
|
+
trivial_patterns: %w[hello hi hey ping pong test ok okay yes no thanks thank],
|
|
386
|
+
exclude_source_agents: %w[teams-api-ingest unknown teams-entity-extractor legion-interlink]
|
|
386
387
|
}
|
|
387
388
|
end
|
|
388
389
|
|
|
@@ -481,9 +482,10 @@ module Legion
|
|
|
481
482
|
|
|
482
483
|
def self.tool_trigger_defaults
|
|
483
484
|
{
|
|
484
|
-
scan_depth:
|
|
485
|
-
tool_limit:
|
|
486
|
-
local_tool_limit:
|
|
485
|
+
scan_depth: 10,
|
|
486
|
+
tool_limit: 25,
|
|
487
|
+
local_tool_limit: 100,
|
|
488
|
+
client_tool_passthrough: false
|
|
487
489
|
}
|
|
488
490
|
end
|
|
489
491
|
|
|
@@ -16,6 +16,7 @@ module Legion
|
|
|
16
16
|
extend Legion::Logging::Helper
|
|
17
17
|
|
|
18
18
|
LIST_SPECIAL_TOOLS_NAME = 'legion_list_special_tools'
|
|
19
|
+
LIST_ALL_TOOLS_NAME = 'legion_list_all_tools'
|
|
19
20
|
DEFAULT_TIMEOUT_MS = 120_000
|
|
20
21
|
MAX_TIMEOUT_MS = 600_000
|
|
21
22
|
PYTHON_PACKAGES = %w[
|
|
@@ -34,7 +35,7 @@ module Legion
|
|
|
34
35
|
module_function
|
|
35
36
|
|
|
36
37
|
def pinned_definitions
|
|
37
|
-
definitions = [special_tools_definition, ruby_definition]
|
|
38
|
+
definitions = [special_tools_definition, all_tools_definition, ruby_definition]
|
|
38
39
|
definitions.concat(python_definitions) if python_available?
|
|
39
40
|
definitions
|
|
40
41
|
end
|
|
@@ -43,6 +44,8 @@ module Legion
|
|
|
43
44
|
case normalize_tool_name(tool_name)
|
|
44
45
|
when LIST_SPECIAL_TOOLS_NAME
|
|
45
46
|
{ status: :success, result: Legion::JSON.dump(inventory) }
|
|
47
|
+
when LIST_ALL_TOOLS_NAME
|
|
48
|
+
{ status: :success, result: Legion::JSON.dump(all_tools_inventory(**args)) }
|
|
46
49
|
when 'ruby'
|
|
47
50
|
dispatch_runtime('ruby', ruby_path, **args)
|
|
48
51
|
when 'python', 'python3'
|
|
@@ -65,6 +68,32 @@ module Legion
|
|
|
65
68
|
}
|
|
66
69
|
end
|
|
67
70
|
|
|
71
|
+
def all_tools_inventory(**args)
|
|
72
|
+
tools = settings_extensions_tools
|
|
73
|
+
extension_filter = args[:extension] || args['extension']
|
|
74
|
+
deferred_filter = args.key?(:deferred) ? args[:deferred] : args['deferred']
|
|
75
|
+
|
|
76
|
+
if extension_filter
|
|
77
|
+
normalized_filter = extension_filter.to_s.tr('-', '_').delete_prefix('lex_')
|
|
78
|
+
tools = tools.select { |t| t[:extension].to_s.tr('-', '_').delete_prefix('lex_').include?(normalized_filter) }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
tools = tools.select { |t| t[:deferred] == deferred_filter } unless deferred_filter.nil?
|
|
82
|
+
|
|
83
|
+
grouped = tools.group_by { |t| t[:extension] || 'unknown' }
|
|
84
|
+
{
|
|
85
|
+
total: tools.size,
|
|
86
|
+
extensions: grouped.transform_values do |ext_tools|
|
|
87
|
+
ext_tools.group_by { |t| t[:runner] || 'default' }.transform_values do |runner_tools|
|
|
88
|
+
runner_tools.map { |t| { name: t[:name], description: t[:description], deferred: t[:deferred] } }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
}
|
|
92
|
+
rescue StandardError => e
|
|
93
|
+
handle_exception(e, level: :warn, handled: true, operation: 'llm.tools.special.all_tools_inventory')
|
|
94
|
+
{ total: 0, extensions: {}, error: e.message }
|
|
95
|
+
end
|
|
96
|
+
|
|
68
97
|
def python_available?
|
|
69
98
|
!python_path.to_s.empty?
|
|
70
99
|
end
|
|
@@ -115,6 +144,22 @@ module Legion
|
|
|
115
144
|
)
|
|
116
145
|
end
|
|
117
146
|
|
|
147
|
+
def all_tools_definition
|
|
148
|
+
Types::ToolDefinition.build(
|
|
149
|
+
name: LIST_ALL_TOOLS_NAME,
|
|
150
|
+
description: 'List ALL registered Legion tools from all loaded extensions, grouped by extension and runner. ' \
|
|
151
|
+
'Use this to discover what tools are available for a specific domain (e.g. Teams, Apollo, identity).',
|
|
152
|
+
parameters: {
|
|
153
|
+
type: 'object',
|
|
154
|
+
properties: {
|
|
155
|
+
extension: { type: 'string', description: 'Filter by extension name (e.g. "microsoft_teams", "apollo"). Omit for all.' },
|
|
156
|
+
deferred: { type: 'boolean', description: 'Filter by deferred status. Omit for all.' }
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
source: { type: :special, handler: :all_tools_inventory, pinned: true }
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
|
|
118
163
|
def ruby_definition
|
|
119
164
|
Types::ToolDefinition.build(
|
|
120
165
|
name: 'ruby',
|
data/lib/legion/llm/version.rb
CHANGED