legion-llm 0.10.1 → 0.10.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94a90f69b3940eb805b6b7bf0b15eb30aa8c616e66562560df01d3f0d5b24ca2
4
- data.tar.gz: 4a90c910e19cec4b3da0e5e44a00d1076495d41d653ad4f7161b3b38892f8d8b
3
+ metadata.gz: f5c1d01be312fb6e070b3e75236a8ecd6c6841653e42eae391cb5b2519a668b6
4
+ data.tar.gz: f6703f5c706dd134e85993c11da4222fc68edced3ca88e09a841f04ca2fb2474
5
5
  SHA512:
6
- metadata.gz: e5f444b505e4b75937223475300cd4bae1e2f292f93148fbb3f0a06446ae6b90b0bf482a7d308a3e580e55219ddc4eb6f0e9274f6479c6e96619d54946cc0ef6
7
- data.tar.gz: 6a9dfedae0e30d61f1bf8c4dc825764e4e9174829cc2d1ea6ee65a00ab7dea2cec5299e5988117aacd24adba93d956970124e00c11a1360bacfab04aa9cf6044
6
+ metadata.gz: 06aef6de56965e58876fa98bfcdf8b411996d00ea3faff44f9aa0f9035d385d9da3591300ac2663a31a2820cf56610572fff3b801ec417af20f20fd377ad0e8a
7
+ data.tar.gz: 77b720ac764fbb899dbbea7d372edfb7a6a8c2671d2b8eef4f92680439d4572e9e8986519c68d4452232ede7c7c87ae74b3b38fbb6383bd9e3dcc1aa0f30670a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Legion LLM Changelog
2
2
 
3
+ ## [0.10.2] - 2026-05-30
4
+
5
+ ### Fixed
6
+ - **Registry tool injection disabled for client passthrough** — When `client_tool_passthrough` is enabled and the request has client-sourced tools (Claude Code, Codex, Kai), Legion no longer injects internal registry tools (`exec_filesystem_mv`, `legion-apollo-*`, etc.) into the payload. This fixes the first-turn garbage output where Qwen3 would output raw tool definition JSON instead of responding normally.
7
+ - **Explicit tool_choice scanning disabled for passthrough** — The vLLM-specific heuristic that scans user message text for tool names and forces `tool_choice` was matching tool names (e.g., "Workflow") inside Claude Code's system prompt (CLAUDE.md embedded in user content). Now skips for client passthrough and messages longer than 500 chars.
3
8
 
4
9
  ## [0.10.1] - 2026-05-29
5
10
 
@@ -63,6 +63,15 @@ module Legion
63
63
  log.info "[llm][api][anthropic] action=request request_id=#{request_id} " \
64
64
  "messages=#{msg_count} chars=#{msg_chars} est_tokens=#{est_tokens} " \
65
65
  "tools=#{tool_defs.size} stream=#{streaming}"
66
+ normalized[:messages].each_with_index do |m, i|
67
+ role = m[:role]
68
+ content = m[:content].is_a?(String) ? m[:content] : m[:content].to_s
69
+ tc = m[:tool_calls]
70
+ tcid = m[:tool_call_id]
71
+ log.debug "[llm][api][anthropic] action=request_msg idx=#{i} role=#{role} " \
72
+ "content_length=#{content.length} content_preview=#{content[0, 120]} " \
73
+ "tool_calls=#{tc&.size || 0} tool_call_id=#{tcid}"
74
+ end
66
75
 
67
76
  executor = Legion::LLM::Inference::Executor.new(pipeline_request)
68
77
  model = body[:model]
@@ -109,6 +118,21 @@ module Legion
109
118
  stop_reason = tool_calls.any? ? 'tool_use' : translator.format_stop_reason(pipeline_response)
110
119
  content_index = 0
111
120
 
121
+ if !text_block_opened && tool_calls.empty?
122
+ fallback_text = extract_fallback_text(pipeline_response)
123
+ unless fallback_text.empty?
124
+ out << "event: content_block_start\ndata: #{Legion::JSON.dump({
125
+ type: 'content_block_start', index: 0,
126
+ content_block: { type: 'text', text: '' }
127
+ })}\n\n"
128
+ out << "event: content_block_delta\ndata: #{Legion::JSON.dump({
129
+ type: 'content_block_delta', index: 0,
130
+ delta: { type: 'text_delta', text: fallback_text }
131
+ })}\n\n"
132
+ text_block_opened = true
133
+ end
134
+ end
135
+
112
136
  log.info "[llm][api][anthropic] action=stream_post request_id=#{request_id} " \
113
137
  "tool_calls=#{tool_calls.size} stop_reason=#{stop_reason} " \
114
138
  "text_block_opened=#{text_block_opened} full_text_length=#{full_text.length}"
@@ -181,6 +205,21 @@ module Legion
181
205
  halt 400, { 'Content-Type' => 'application/json' },
182
206
  Legion::JSON.dump({ type: 'error', error: { type: 'invalid_request_error', message: "missing required fields: #{missing.join(', ')}" } })
183
207
  end
208
+
209
+ def extract_fallback_text(pipeline_response)
210
+ msg = pipeline_response.message
211
+ text = msg.is_a?(Hash) ? (msg[:content] || msg['content']).to_s : ''
212
+ return text unless text.empty?
213
+ return '' unless pipeline_response.respond_to?(:thinking) && pipeline_response.thinking
214
+
215
+ thinking_data = pipeline_response.thinking
216
+ thinking_content = if thinking_data.is_a?(Hash)
217
+ thinking_data[:content] || thinking_data['content']
218
+ elsif thinking_data.respond_to?(:content)
219
+ thinking_data.content
220
+ end
221
+ thinking_content.to_s.strip
222
+ end
184
223
  end
185
224
  end
186
225
  end
@@ -983,7 +983,19 @@ module Legion
983
983
  end
984
984
 
985
985
  def registry_tool_injection_requested?
986
- !(@request.respond_to?(:suppress_tools) && @request.suppress_tools)
986
+ return false if @request.respond_to?(:suppress_tools) && @request.suppress_tools
987
+ return false if client_tools_only?
988
+
989
+ true
990
+ end
991
+
992
+ def client_tools_only?
993
+ return false unless client_tool_passthrough_enabled?
994
+
995
+ Array(@request.tools).any? do |tool|
996
+ source = tool.respond_to?(:source) ? tool.source : {}
997
+ source.is_a?(Hash) && source[:type] == :client
998
+ end
987
999
  end
988
1000
 
989
1001
  def client_tool_passthrough_enabled?
@@ -110,9 +110,10 @@ module Legion
110
110
 
111
111
  def explicit_native_tool_choice
112
112
  return unless @resolved_provider.to_s == 'vllm'
113
+ return if client_tools_only?
113
114
 
114
115
  text = latest_user_text.to_s.downcase
115
- return if text.empty?
116
+ return if text.empty? || text.length > 500
116
117
 
117
118
  native_dispatch_tools.keys.map(&:to_s).sort_by { |tool_name| -tool_name.length }.find do |tool_name|
118
119
  explicit_tool_name_mentioned?(text, tool_name)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module LLM
5
- VERSION = '0.10.1'
5
+ VERSION = '0.10.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.10.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity