legion-tty 0.5.2 → 0.5.4
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 +12 -5
- data/lib/legion/tty/app.rb +1 -1
- data/lib/legion/tty/background/llm_probe.rb +3 -1
- data/lib/legion/tty/components/message_stream.rb +25 -2
- data/lib/legion/tty/screens/chat.rb +2 -4
- data/lib/legion/tty/screens/extensions.rb +10 -3
- data/lib/legion/tty/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: 2c188384bc3e940bfb528a15116ef146f721829b0bfc70bbc1df623795d42d19
|
|
4
|
+
data.tar.gz: '086e3dce92e450f34464e3c55136241f160313f5c52538ff5a6368e1df10ce14'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1ba2062dd30d8c912fe163a2d2a0161b70169779f7cd54d94668a3e8ffab9ec463e1e4c3038f47054cda4e1c1d392ca1fe4a378fc630b2efc4ba829a35ff8563
|
|
7
|
+
data.tar.gz: c7240dfdd607a8c04bfd2b449863330e48b563d54d778976d974ddf83afe5ccc67ff8cc68f0bc71fba9f78fc6bd10bb03efa15f99fbfe11542812370b9d751cd
|
data/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [0.5.
|
|
3
|
+
## [0.5.4] - 2026-04-28
|
|
4
4
|
|
|
5
5
|
### Fixed
|
|
6
|
-
- **
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
-
|
|
6
|
+
- **LLM provider probe routing** — `Background::LlmProbe` now validates enabled providers through `Legion::LLM.ask` with the configured provider/model instead of calling RubyLLM directly.
|
|
7
|
+
- **Extension catalog provider stack** — the extensions screen now categorizes native `lex-llm-*` provider gems as AI extensions and shows `lex-llm-gateway` as legacy compatibility instead of an active Core extension.
|
|
8
|
+
- **Future native LLM providers** — the extensions screen now classifies future `lex-llm-*` provider gems as AI automatically while preserving `lex-llm-gateway` as Legacy.
|
|
9
|
+
- **Development dependency cleanup** — removed the stale direct `ruby_llm` Gemfile dependency so local TTY development follows the Legion-native LLM path.
|
|
10
|
+
|
|
11
|
+
## [0.5.3] - 2026-04-20
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- **Scroll performance** — `MessageStream` now caches rendered lines per message keyed on `(object_id, content_hash, role, width, reactions, annotations, tags, pinned)`; cache invalidates automatically on content change (streaming), display option change, or width change — markdown re-parsing only runs for new/changed messages (closes #23)
|
|
15
|
+
- **Input coalescing** — `App#run_loop` drains all queued keys before the next `render_frame`, so rapid typing no longer triggers O(total-messages) renders per keystroke (closes #23)
|
|
16
|
+
- **PgUp/PgDn scroll step** — now scrolls by half-viewport (`(height-3)/2`) instead of a fixed 10 lines, matching Ctrl+B/Ctrl+F behavior (closes #23)
|
|
10
17
|
|
|
11
18
|
## [0.5.1] - 2026-04-18
|
|
12
19
|
|
data/lib/legion/tty/app.rb
CHANGED
|
@@ -542,7 +542,7 @@ module Legion
|
|
|
542
542
|
|
|
543
543
|
def try_settings_llm
|
|
544
544
|
# All LLM calls route through the LegionIO daemon API.
|
|
545
|
-
# No raw
|
|
545
|
+
# No raw provider session is created here; nil signals "use daemon path".
|
|
546
546
|
if Legion::TTY::DaemonClient.available?
|
|
547
547
|
log.debug { 'TTY: daemon available, LLM routed through daemon' }
|
|
548
548
|
else
|
|
@@ -8,6 +8,8 @@ module Legion
|
|
|
8
8
|
class LlmProbe
|
|
9
9
|
include Legion::Logging::Helper
|
|
10
10
|
|
|
11
|
+
PROBE_PROMPT = 'Respond with only: pong'
|
|
12
|
+
|
|
11
13
|
def initialize(logger: nil, wait_queue: nil)
|
|
12
14
|
@boot_log = logger
|
|
13
15
|
@wait_queue = wait_queue
|
|
@@ -76,7 +78,7 @@ module Legion
|
|
|
76
78
|
def ping_provider(name, config)
|
|
77
79
|
model = config[:default_model]
|
|
78
80
|
start_time = Time.now
|
|
79
|
-
|
|
81
|
+
Legion::LLM.ask(message: PROBE_PROMPT, model: model, provider: name)
|
|
80
82
|
latency = ((Time.now - start_time) * 1000).round
|
|
81
83
|
{ name: name, model: model, status: :ok, latency_ms: latency }
|
|
82
84
|
rescue StandardError => e
|
|
@@ -28,10 +28,13 @@ module Legion
|
|
|
28
28
|
@show_numbers = false
|
|
29
29
|
@colorize = true
|
|
30
30
|
@show_timestamps = true
|
|
31
|
+
@line_cache = {}
|
|
32
|
+
@cache_options_hash = nil
|
|
31
33
|
end
|
|
32
34
|
|
|
33
35
|
def add_message(role:, content:)
|
|
34
36
|
@messages << { role: role, content: content, tool_panels: [], timestamp: Time.now }
|
|
37
|
+
compact_cache if @line_cache.size > @messages.size * 2
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
def append_streaming(text)
|
|
@@ -87,15 +90,35 @@ module Legion
|
|
|
87
90
|
|
|
88
91
|
private
|
|
89
92
|
|
|
90
|
-
def build_all_lines(width)
|
|
93
|
+
def build_all_lines(width) # rubocop:disable Metrics/AbcSize
|
|
94
|
+
current_opts = options_hash
|
|
95
|
+
if current_opts != @cache_options_hash
|
|
96
|
+
@line_cache.clear
|
|
97
|
+
@cache_options_hash = current_opts
|
|
98
|
+
end
|
|
99
|
+
|
|
91
100
|
filtered_messages.each_with_index.flat_map do |msg, idx|
|
|
92
101
|
next [] if @mute_system && msg[:role] == :system
|
|
93
102
|
next [] if @silent_mode && msg[:role] == :assistant
|
|
94
103
|
|
|
95
|
-
|
|
104
|
+
cache_key = message_cache_key(msg, width)
|
|
105
|
+
@line_cache[cache_key] ||= render_message(msg, width, @show_numbers ? idx + 1 : nil)
|
|
96
106
|
end
|
|
97
107
|
end
|
|
98
108
|
|
|
109
|
+
def options_hash
|
|
110
|
+
[@wrap_width, @show_numbers, @colorize, @show_timestamps, @highlights, @truncate_limit].hash
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def message_cache_key(msg, width)
|
|
114
|
+
[msg.object_id, msg[:content], msg[:role], width,
|
|
115
|
+
msg[:reactions], msg[:annotations], msg[:tags], msg[:pinned]].hash
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def compact_cache
|
|
119
|
+
@line_cache.clear
|
|
120
|
+
end
|
|
121
|
+
|
|
99
122
|
def filtered_messages
|
|
100
123
|
return @messages if @filter.nil?
|
|
101
124
|
|
|
@@ -208,12 +208,10 @@ module Legion
|
|
|
208
208
|
|
|
209
209
|
def handle_scroll_key(key)
|
|
210
210
|
case key
|
|
211
|
-
when :page_up
|
|
212
|
-
when :page_down
|
|
211
|
+
when :page_up, :ctrl_b then @message_stream.scroll_up(half_page_lines)
|
|
212
|
+
when :page_down, :ctrl_f then @message_stream.scroll_down(half_page_lines)
|
|
213
213
|
when :scroll_up then @message_stream.scroll_up(3)
|
|
214
214
|
when :scroll_down then @message_stream.scroll_down(3)
|
|
215
|
-
when :ctrl_b then @message_stream.scroll_up(half_page_lines)
|
|
216
|
-
when :ctrl_f then @message_stream.scroll_down(half_page_lines)
|
|
217
215
|
when :home then @message_stream.scroll_up(@message_stream.messages.size * 5)
|
|
218
216
|
when :end then @message_stream.scroll_down(@message_stream.scroll_offset)
|
|
219
217
|
else return nil
|
|
@@ -12,15 +12,20 @@ module Legion
|
|
|
12
12
|
include Legion::Logging::Helper
|
|
13
13
|
|
|
14
14
|
CORE = %w[lex-node lex-tasker lex-scheduler lex-conditioner lex-transformer
|
|
15
|
-
lex-synapse lex-health lex-log lex-ping lex-metering lex-llm-
|
|
15
|
+
lex-synapse lex-health lex-log lex-ping lex-metering lex-llm-ledger
|
|
16
16
|
lex-codegen lex-exec lex-lex lex-telemetry lex-audit lex-detect].freeze
|
|
17
17
|
|
|
18
|
-
AI = %w[lex-claude lex-
|
|
18
|
+
AI = %w[lex-azure-ai lex-bedrock lex-claude lex-foundry lex-gemini lex-llamacpp
|
|
19
|
+
lex-mlx lex-ollama lex-openai lex-uais lex-xai lex-llm lex-llm-anthropic
|
|
20
|
+
lex-llm-azure-foundry lex-llm-bedrock lex-llm-gemini lex-llm-mlx
|
|
21
|
+
lex-llm-ollama lex-llm-openai lex-llm-vertex lex-llm-vllm].freeze
|
|
22
|
+
|
|
23
|
+
LEGACY = %w[lex-llm-gateway].freeze
|
|
19
24
|
|
|
20
25
|
SERVICE = %w[lex-http lex-vault lex-github lex-consul lex-kerberos lex-tfe
|
|
21
26
|
lex-redis lex-memcached lex-elasticsearch lex-s3].freeze
|
|
22
27
|
|
|
23
|
-
CATEGORIES = [nil, 'Core', 'AI', 'Service', 'Agentic', 'Other'].freeze
|
|
28
|
+
CATEGORIES = [nil, 'Core', 'AI', 'Service', 'Agentic', 'Legacy', 'Other'].freeze
|
|
24
29
|
|
|
25
30
|
def initialize(app, output: $stdout)
|
|
26
31
|
super(app)
|
|
@@ -116,6 +121,8 @@ module Legion
|
|
|
116
121
|
|
|
117
122
|
def categorize(name)
|
|
118
123
|
return 'Core' if CORE.include?(name)
|
|
124
|
+
return 'Legacy' if LEGACY.include?(name)
|
|
125
|
+
return 'AI' if name.start_with?('lex-llm-')
|
|
119
126
|
return 'AI' if AI.include?(name)
|
|
120
127
|
return 'Service' if SERVICE.include?(name)
|
|
121
128
|
return 'Agentic' if name.match?(/^lex-agentic-|^lex-theory-|^lex-mind-|^lex-planning|^lex-attention/)
|
data/lib/legion/tty/version.rb
CHANGED