lex-llm 0.6.9 → 0.6.10
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: e441fc6b1e568e1e7cde529aecb14655cfc6df845accdfbf706cade536faa25e
|
|
4
|
+
data.tar.gz: fa14725026a56fe266e5c7b4be232fc1b750bf35293830514a6ad103b1da3abc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f14ac394687d2059d90562529518085a4cced1732e9baa85c5847ad2fffd4ea89efeb735167ebae6533d4b335f9ddd7fcc8982c0bdac137add3d35a48010b19e
|
|
7
|
+
data.tar.gz: ed81c83224bdcbbf18b0b4fc6f8b9e2bf0886ed44d80a2dde363d4920a3a4957d4f56f34429b6ea7b7fd5ba96fd4a5b5e60fb9bc0e59cca556b1ed11e7314080
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.10 - 2026-07-09
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `ThinkingConfig#resolved_budget` and `#resolved_effort` — cross-axis derivation so provider translators get whichever thinking axis they need (Anthropic `budget_tokens` vs OpenAI `effort`) even when the client supplied only the other. A client dialect supplies only one axis (Anthropic = budget only; OpenAI = effort only), so a single `EFFORT_BUDGET` map (SSOT) drives the conversion in both directions: effort -> budget is exact (`low` = 1024, `medium` = 8192, `high` = 16384), budget -> effort maps by band boundary. Explicitly-set values always win over derivation, and both accessors return `nil` when neither axis is configured. `to_h` stays faithful to what was actually set (no fabrication). This is the foundation for fixing thinking being silently dropped on cross-provider routes (e.g. a Claude budget request routed to OpenAI effort).
|
|
7
|
+
|
|
3
8
|
## 0.6.9 - 2026-07-05
|
|
4
9
|
|
|
5
10
|
### Added
|
|
@@ -50,6 +50,16 @@ module Legion
|
|
|
50
50
|
# Mirrors lex-llm Thinking::Config.
|
|
51
51
|
class ThinkingConfig
|
|
52
52
|
INCLUDES = Thinking
|
|
53
|
+
|
|
54
|
+
# SSOT for the effort<->budget conversion. A client dialect supplies only
|
|
55
|
+
# ONE axis (Anthropic = budget_tokens only; OpenAI = effort only), but a
|
|
56
|
+
# provider translator may need the OTHER. This single map lets every
|
|
57
|
+
# provider ask for whichever axis it needs and always get a usable value,
|
|
58
|
+
# so thinking survives any client x provider pair (best-effort, never
|
|
59
|
+
# silently dropped). effort -> budget is exact; budget -> effort uses the
|
|
60
|
+
# band boundaries below.
|
|
61
|
+
EFFORT_BUDGET = { 'low' => 1024, 'medium' => 8192, 'high' => 16_384 }.freeze
|
|
62
|
+
|
|
53
63
|
attr_reader :effort, :budget
|
|
54
64
|
|
|
55
65
|
def initialize(effort: nil, budget: nil)
|
|
@@ -70,7 +80,8 @@ module Legion
|
|
|
70
80
|
build(effort: h[:effort], budget: h[:budget])
|
|
71
81
|
end
|
|
72
82
|
|
|
73
|
-
# Serialize to a Hash for AMQP/fleet/wire transport.
|
|
83
|
+
# Serialize to a Hash for AMQP/fleet/wire transport. Faithful to what was
|
|
84
|
+
# SET — never fabricates the missing axis (use resolved_* for that).
|
|
74
85
|
def to_h
|
|
75
86
|
{ effort: effort, budget: budget }.compact
|
|
76
87
|
end
|
|
@@ -79,6 +90,30 @@ module Legion
|
|
|
79
90
|
def enabled?
|
|
80
91
|
!effort.nil? || !budget.nil?
|
|
81
92
|
end
|
|
93
|
+
|
|
94
|
+
# Budget for a provider that needs a token budget (e.g. Anthropic),
|
|
95
|
+
# derived from effort when budget was not explicitly set. nil only when
|
|
96
|
+
# neither axis is configured.
|
|
97
|
+
def resolved_budget
|
|
98
|
+
return budget unless budget.nil?
|
|
99
|
+
return nil if effort.nil?
|
|
100
|
+
|
|
101
|
+
EFFORT_BUDGET[effort.to_s.downcase] || EFFORT_BUDGET['medium']
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Effort for a provider that needs an effort level (e.g. OpenAI),
|
|
105
|
+
# derived from budget when effort was not explicitly set. nil only when
|
|
106
|
+
# neither axis is configured.
|
|
107
|
+
def resolved_effort
|
|
108
|
+
return effort unless effort.nil?
|
|
109
|
+
return nil if budget.nil?
|
|
110
|
+
|
|
111
|
+
b = budget.to_i
|
|
112
|
+
if b < EFFORT_BUDGET['medium'] then 'low'
|
|
113
|
+
elsif b < EFFORT_BUDGET['high'] then 'medium'
|
|
114
|
+
else 'high'
|
|
115
|
+
end
|
|
116
|
+
end
|
|
82
117
|
end
|
|
83
118
|
|
|
84
119
|
# Alias for convenience: Canonical::Thinking::Config
|
|
@@ -147,5 +147,43 @@ RSpec.describe Legion::Extensions::Llm::Canonical::Thinking do
|
|
|
147
147
|
expect(config.to_h).to eq(effort: 'low')
|
|
148
148
|
end
|
|
149
149
|
end
|
|
150
|
+
|
|
151
|
+
# SSOT / one-oracle for cross-provider thinking (NxN best-effort): a client
|
|
152
|
+
# dialect supplies only ONE axis (Anthropic = budget_tokens only, OpenAI =
|
|
153
|
+
# effort only). Every provider translator must still get a usable value for
|
|
154
|
+
# whichever axis IT needs, so thinking survives ANY client×provider pair
|
|
155
|
+
# instead of being silently dropped. The conversion lives HERE (the canonical
|
|
156
|
+
# config), not scattered/one-directional in each translator. Derived accessors
|
|
157
|
+
# do NOT mutate stored state or #to_h — they only fill the gap on read.
|
|
158
|
+
describe 'derived cross-axis accessors' do
|
|
159
|
+
it 'derives budget from effort when only effort was set' do
|
|
160
|
+
expect(config_class.build(effort: 'low').resolved_budget).to eq(1024)
|
|
161
|
+
expect(config_class.build(effort: 'medium').resolved_budget).to eq(8192)
|
|
162
|
+
expect(config_class.build(effort: 'high').resolved_budget).to eq(16_384)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'derives effort from budget by band (< medium=low, < high=medium, else high)' do
|
|
166
|
+
expect(config_class.build(budget: 500).resolved_effort).to eq('low') # < 8192
|
|
167
|
+
expect(config_class.build(budget: 10_000).resolved_effort).to eq('medium') # 8192..16383
|
|
168
|
+
expect(config_class.build(budget: 20_000).resolved_effort).to eq('high') # >= 16384
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it 'prefers the explicitly-set value over derivation' do
|
|
172
|
+
c = config_class.build(effort: 'high', budget: 2048)
|
|
173
|
+
expect(c.resolved_budget).to eq(2048)
|
|
174
|
+
expect(c.resolved_effort).to eq('high')
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it 'returns nil derivations when neither axis is set' do
|
|
178
|
+
c = config_class.build
|
|
179
|
+
expect(c.resolved_budget).to be_nil
|
|
180
|
+
expect(c.resolved_effort).to be_nil
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'does not fabricate the missing axis in to_h (stored state stays faithful)' do
|
|
184
|
+
expect(config_class.build(effort: 'high').to_h).to eq(effort: 'high')
|
|
185
|
+
expect(config_class.build(budget: 2048).to_h).to eq(budget: 2048)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
150
188
|
end
|
|
151
189
|
end
|