legion-llm 0.14.15 → 0.14.16

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: 6720d66411b7d2125aab243354ca9f0bf19292b10895695c7e762fe6f23180d0
4
- data.tar.gz: 87910fc8387d28d65759e207f79bf6d1a2b39e178a4fefe5c8b6bd5f17187f6b
3
+ metadata.gz: 3eec433896cf314b65ac64111f414a55dc7e9639db3d54cc76359c9fb01ed567
4
+ data.tar.gz: 659857c6aa71573f6fbb795a655b7a59f2936672b0538db0bc14c18ae06ecfbd
5
5
  SHA512:
6
- metadata.gz: 83e7680e3083b13e523f66cdba62ff28680f85bc4e63d0e59ef15b4d7cb260c28c02d1325fa76566f5bd8a6606dc55d552bce9dbf097d45d197a02a2a486ac9d
7
- data.tar.gz: 9cbc000d34b4afdc8e24dc677ea9a0a22a397868bf1e765d96e2bf7ea173016d293441dbdb6401edd2de30380dd0892cfd73e65550797aca1f4fdaa81e08db0d
6
+ metadata.gz: 648b2bd80386141477bf5b77b387814f768d5f64447a921d34ef828dde6d9469a15e63211e63c4173e7188e0a61c5d60eb366415ccb68c43c031a23cd4889ac3
7
+ data.tar.gz: 22f1a949a48470e5a6b543a683e2de4f9c86f44d1c21d706e704bb4a5f58672311ba632b03710647beb071987abed2947e6299e0ad69c06b73c72484398fb238
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Legion LLM Changelog
2
2
 
3
+ ## [0.14.16] - 2026-07-14
4
+
5
+ ### Fixed
6
+
7
+ - **Server tool results are now visible to Claude and Codex streaming clients.** When the daemon executes a LegionIO tool server-side during the streaming tool loop, the result must reach the client in a format its SDK can parse. Two bugs prevented this:
8
+ - **Anthropic Messages (`/v1/messages`):** The `server_tool_result` SSE emitted `content: []` in `content_block_start` and put the actual result in a `content_block_delta` with `delta.type = "content_block_delta"` — a type the Anthropic TypeScript/Python SDK does not process. The client saw an empty result block. Fixed: result content is now placed directly in the `content_block_start` event; no unrecognized delta needed.
9
+ - **OpenAI Responses (`/v1/responses`):** The `on_server_tool_result` emitter was a no-op — tool results were silently discarded. Codex saw `function_call` items with no matching `function_call_output`. Fixed: emits a proper `response.output_item.added` + `response.output_item.done` pair with `type: "function_call_output"`, `call_id`, and the result text.
10
+
11
+ - **Empty assistant messages left after thinking extraction are now stripped.** `strip_thinking_from_history` can leave behind assistant messages with no content and no tool_calls (the thinking was their only payload). These empty messages confused some providers. A second `empty_assistant_message?` pass now runs after thinking stripping.
12
+
3
13
  ## [0.14.15] - 2026-07-09
4
14
 
5
15
  ### Fixed
@@ -292,12 +292,8 @@ module Legion
292
292
  emit('content_block_start', {
293
293
  type: 'content_block_start',
294
294
  index: block_index,
295
- content_block: { type: 'server_tool_result', id: tool_call_id, content: [] }
296
- })
297
- emit('content_block_delta', {
298
- type: 'content_block_delta',
299
- index: block_index,
300
- delta: { type: 'content_block_delta', content: [{ type: 'text', text: result_text }] }
295
+ content_block: { type: 'server_tool_result', id: tool_call_id,
296
+ content: [{ type: 'text', text: result_text }] }
301
297
  })
302
298
  emit('content_block_stop', { type: 'content_block_stop', index: block_index })
303
299
  end
@@ -401,11 +401,18 @@ module Legion
401
401
 
402
402
  def on_server_tool_result(block_index:, tool_call_id:, result_text:)
403
403
  _ = block_index
404
- _ = tool_call_id
405
- _ = result_text
406
- # /v1/responses doesn't expose server tool results inline today —
407
- # the function_call output item carries id/args; result is part
408
- # of the assistant message that follows on the next turn.
404
+ idx = @output_items.length
405
+ item = { id: "fco_#{tool_call_id}", type: 'function_call_output',
406
+ call_id: tool_call_id, output: result_text.to_s, status: 'completed' }
407
+ @output_items << item
408
+ emit('response.output_item.added', {
409
+ type: 'response.output_item.added', sequence_number: next_seq,
410
+ output_index: idx, item: item
411
+ })
412
+ emit('response.output_item.done', {
413
+ type: 'response.output_item.done', sequence_number: next_seq,
414
+ output_index: idx, item: item
415
+ })
409
416
  end
410
417
 
411
418
  def on_keep_alive
@@ -17,6 +17,11 @@ module Legion
17
17
  messages = messages.reject { |m| empty_assistant_message?(m) }
18
18
  end
19
19
  messages = strip_thinking_from_history(messages)
20
+ post_thinking_rejected = messages.count { |m| empty_assistant_message?(m) }
21
+ if post_thinking_rejected.positive?
22
+ log.warn "[llm][executor] action=strip_empty_assistants_post_thinking request_id=#{@request.id} removed=#{post_thinking_rejected}"
23
+ messages = messages.reject { |m| empty_assistant_message?(m) }
24
+ end
20
25
  messages = trim_oversized_tool_results(messages)
21
26
  enforce_context_window(messages)
22
27
  end
@@ -170,12 +170,12 @@ module Legion
170
170
  end
171
171
 
172
172
  def pipeline_escalation_enabled?
173
- esc = Legion::Settings[:llm].dig(:routing, :escalation) || {}
173
+ esc = Legion::Settings[:llm][:routing][:escalation]
174
174
  esc[:enabled] == true && esc[:pipeline_enabled] == true
175
175
  end
176
176
 
177
177
  def pipeline_escalation_max_attempts
178
- esc = Legion::Settings[:llm].dig(:routing, :escalation) || {}
178
+ esc = Legion::Settings[:llm][:routing][:escalation]
179
179
  esc[:max_attempts] || 3
180
180
  end
181
181
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module LLM
5
- VERSION = '0.14.15'
5
+ VERSION = '0.14.16'
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.14.15
4
+ version: 0.14.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity