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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5c1d01be312fb6e070b3e75236a8ecd6c6841653e42eae391cb5b2519a668b6
|
|
4
|
+
data.tar.gz: f6703f5c706dd134e85993c11da4222fc68edced3ca88e09a841f04ca2fb2474
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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)
|
data/lib/legion/llm/version.rb
CHANGED