lex-llm 0.6.8 → 0.6.9
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: d325676c4cc272b839d2e0ba4d678a1aab7ec05268e5e7d37ac302234d25ae4e
|
|
4
|
+
data.tar.gz: a8a2475af20b60ea5d53dc2c5da7ceb0ea2f25d8c094079df73b41cce04ea577
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5535f26e2755347ca1024c662eeb7e575515bfa824fe36a2f3958558d21548f393b426f0261a35f3935ccd222099ad8dedaef614af693a0b2d0a74e2353e91ae
|
|
7
|
+
data.tar.gz: 59fd2029653e72366a5384d36f34639348b12ecba38ba3e2ad19ff0a20e5ffcf6d7ea434e02fcfc8994fa0f373264efac90d532800cd3ae14f2036f10f9f64c8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.9 - 2026-07-05
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `StopReasonMapping` mixin (`legion/extensions/llm/stop_reason_mapping`) — a shared stop_reason vocabulary included by provider translators (and available to legion-llm, which depends on lex-llm). Keys are incoming provider wire strings, values are the canonical `stop_reason` symbol. Provider wire formats spell the same six canonical end-states differently (OpenAI/vLLM emit `tool_calls`, Anthropic `tool_use`; `stop`/`end_turn`/`eos` all mean the same thing), so the common vocabulary now lives in one place instead of being copy-pasted (and drifting) across provider gems.
|
|
7
|
+
- `#stop_reason_map` — the common vocabulary, inherited by all.
|
|
8
|
+
- `#stop_reason_map_additions` — returns `{}` in the base; a provider overrides it to ADD provider-specific strings (merged on top, additions win on collision). No guards needed.
|
|
9
|
+
- `#stop_reason_lookup(key)` — merges additions over the map, coerces the key via `to_s`, returns the canonical symbol or `nil` (caller decides the default). To REPLACE the whole vocabulary, a provider overrides `#stop_reason_map`.
|
|
10
|
+
|
|
3
11
|
## 0.6.8 - 2026-07-03
|
|
4
12
|
|
|
5
13
|
### Changed
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Llm
|
|
6
|
+
# Shared stop_reason vocabulary, included by every provider translator (and
|
|
7
|
+
# available to legion-llm, which depends on lex-llm). Provider wire formats
|
|
8
|
+
# spell the same canonical end-states differently — OpenAI/vLLM emit
|
|
9
|
+
# "tool_calls", Anthropic "tool_use", and "stop"/"end_turn"/"eos" all mean
|
|
10
|
+
# the same thing. This puts the common vocabulary in one place so it is not
|
|
11
|
+
# copy-pasted (and drifting) across five provider gems.
|
|
12
|
+
#
|
|
13
|
+
# Keys are the incoming provider strings; values are the canonical
|
|
14
|
+
# stop_reason (see Canonical::Response::STOP_REASONS).
|
|
15
|
+
#
|
|
16
|
+
# Provider overrides, all by plain method definition — no guards:
|
|
17
|
+
# * ADD provider-specific strings → override #stop_reason_map_additions
|
|
18
|
+
# * REPLACE the whole vocabulary → override #stop_reason_map
|
|
19
|
+
module StopReasonMapping
|
|
20
|
+
# The common vocabulary every provider inherits for free.
|
|
21
|
+
def stop_reason_map
|
|
22
|
+
{
|
|
23
|
+
'stop' => :end_turn,
|
|
24
|
+
'end_turn' => :end_turn,
|
|
25
|
+
'eos' => :end_turn,
|
|
26
|
+
'complete' => :end_turn,
|
|
27
|
+
'tool_calls' => :tool_use,
|
|
28
|
+
'tool_call' => :tool_use,
|
|
29
|
+
'tool_use' => :tool_use,
|
|
30
|
+
'function_call' => :tool_use,
|
|
31
|
+
'length' => :max_tokens,
|
|
32
|
+
'max_tokens' => :max_tokens,
|
|
33
|
+
'stop_sequence' => :stop_sequence,
|
|
34
|
+
'stop_sequences' => :stop_sequence,
|
|
35
|
+
'content_filter' => :content_filter,
|
|
36
|
+
'error' => :error
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Provider-specific additions, merged on top of the common map. Base
|
|
41
|
+
# returns {} so callers never need an "if defined?" guard. Additions win
|
|
42
|
+
# over the common map on key collision.
|
|
43
|
+
def stop_reason_map_additions
|
|
44
|
+
{}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Resolve a provider wire stop_reason string to its canonical symbol, or
|
|
48
|
+
# nil when unmapped (the caller decides the default — usually :end_turn).
|
|
49
|
+
def stop_reason_lookup(key)
|
|
50
|
+
stop_reason_map.merge(stop_reason_map_additions).fetch(key.to_s, nil)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
# Shared stop_reason vocabulary. Provider wire formats spell the same six
|
|
6
|
+
# canonical end-states differently (OpenAI/vLLM: "tool_calls"; Anthropic:
|
|
7
|
+
# "tool_use"; "stop" vs "end_turn" vs "eos"). Before this module, every
|
|
8
|
+
# provider translator carried its own copy-pasted *_STOP_REASON_MAP, which
|
|
9
|
+
# drifted — vllm/ollama mapped only "tool_use" and silently fell back to
|
|
10
|
+
# :end_turn on the "tool_calls" that OpenAI-compatible backends actually send.
|
|
11
|
+
#
|
|
12
|
+
# StopReasonMapping puts the common vocabulary in one place (inherited by all)
|
|
13
|
+
# with a provider-overridable additions hook and a full-replace hook.
|
|
14
|
+
RSpec.describe Legion::Extensions::Llm::StopReasonMapping do
|
|
15
|
+
let(:host_class) do
|
|
16
|
+
Class.new { include Legion::Extensions::Llm::StopReasonMapping }
|
|
17
|
+
end
|
|
18
|
+
let(:host) { host_class.new }
|
|
19
|
+
|
|
20
|
+
describe 'the common vocabulary (#stop_reason_lookup)' do
|
|
21
|
+
it 'maps OpenAI/vLLM "tool_calls" to :tool_use' do
|
|
22
|
+
expect(host.stop_reason_lookup('tool_calls')).to eq(:tool_use)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'maps Anthropic "tool_use" to :tool_use' do
|
|
26
|
+
expect(host.stop_reason_lookup('tool_use')).to eq(:tool_use)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'maps "stop"/"end_turn"/"eos" to :end_turn' do
|
|
30
|
+
expect(host.stop_reason_lookup('stop')).to eq(:end_turn)
|
|
31
|
+
expect(host.stop_reason_lookup('end_turn')).to eq(:end_turn)
|
|
32
|
+
expect(host.stop_reason_lookup('eos')).to eq(:end_turn)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'maps "length"/"max_tokens" to :max_tokens' do
|
|
36
|
+
expect(host.stop_reason_lookup('length')).to eq(:max_tokens)
|
|
37
|
+
expect(host.stop_reason_lookup('max_tokens')).to eq(:max_tokens)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'coerces non-string keys via to_s' do
|
|
41
|
+
expect(host.stop_reason_lookup(:tool_calls)).to eq(:tool_use)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'returns nil for an unmapped key (caller decides the default)' do
|
|
45
|
+
expect(host.stop_reason_lookup('totally_unknown')).to be_nil
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe 'provider additions (#stop_reason_map_additions)' do
|
|
50
|
+
it 'base returns {} so no guard is ever needed' do
|
|
51
|
+
expect(host.stop_reason_map_additions).to eq({})
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'a provider adds provider-specific strings on top of the common map' do
|
|
55
|
+
bedrock_like = Class.new do
|
|
56
|
+
include Legion::Extensions::Llm::StopReasonMapping
|
|
57
|
+
|
|
58
|
+
def stop_reason_map_additions
|
|
59
|
+
{ 'guardrail_intervened' => :content_filter }
|
|
60
|
+
end
|
|
61
|
+
end.new
|
|
62
|
+
|
|
63
|
+
# addition resolves
|
|
64
|
+
expect(bedrock_like.stop_reason_lookup('guardrail_intervened')).to eq(:content_filter)
|
|
65
|
+
# common vocabulary still inherited
|
|
66
|
+
expect(bedrock_like.stop_reason_lookup('tool_calls')).to eq(:tool_use)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'additions win over the common map on key collision' do
|
|
70
|
+
override = Class.new do
|
|
71
|
+
include Legion::Extensions::Llm::StopReasonMapping
|
|
72
|
+
|
|
73
|
+
def stop_reason_map_additions
|
|
74
|
+
{ 'stop' => :stop_sequence }
|
|
75
|
+
end
|
|
76
|
+
end.new
|
|
77
|
+
|
|
78
|
+
expect(override.stop_reason_lookup('stop')).to eq(:stop_sequence)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe 'full replacement (#stop_reason_map)' do
|
|
83
|
+
it 'a provider can replace the whole map, dropping the defaults' do
|
|
84
|
+
replaced = Class.new do
|
|
85
|
+
include Legion::Extensions::Llm::StopReasonMapping
|
|
86
|
+
|
|
87
|
+
def stop_reason_map
|
|
88
|
+
{ 'weird_only' => :end_turn }
|
|
89
|
+
end
|
|
90
|
+
end.new
|
|
91
|
+
|
|
92
|
+
expect(replaced.stop_reason_lookup('weird_only')).to eq(:end_turn)
|
|
93
|
+
expect(replaced.stop_reason_lookup('tool_calls')).to be_nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-llm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LegionIO
|
|
@@ -324,6 +324,7 @@ files:
|
|
|
324
324
|
- lib/legion/extensions/llm/routing/model_offering.rb
|
|
325
325
|
- lib/legion/extensions/llm/routing/offering_registry.rb
|
|
326
326
|
- lib/legion/extensions/llm/routing/registry_event.rb
|
|
327
|
+
- lib/legion/extensions/llm/stop_reason_mapping.rb
|
|
327
328
|
- lib/legion/extensions/llm/stream_accumulator.rb
|
|
328
329
|
- lib/legion/extensions/llm/streaming.rb
|
|
329
330
|
- lib/legion/extensions/llm/taxonomies.rb
|
|
@@ -422,6 +423,7 @@ files:
|
|
|
422
423
|
- spec/legion/extensions/llm/routing/model_offering_spec.rb
|
|
423
424
|
- spec/legion/extensions/llm/routing/offering_registry_spec.rb
|
|
424
425
|
- spec/legion/extensions/llm/routing/registry_event_spec.rb
|
|
426
|
+
- spec/legion/extensions/llm/stop_reason_mapping_spec.rb
|
|
425
427
|
- spec/legion/extensions/llm/stream_accumulator_spec.rb
|
|
426
428
|
- spec/legion/extensions/llm/streaming_spec.rb
|
|
427
429
|
- spec/legion/extensions/llm/taxonomies_spec.rb
|