ruby_llm-contract 0.8.0 → 0.10.1
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 +4 -4
- data/CHANGELOG.md +64 -0
- data/Gemfile.lock +2 -2
- data/README.md +96 -37
- data/lib/ruby_llm/contract/adapters/ruby_llm.rb +9 -1
- data/lib/ruby_llm/contract/concerns/eval_host.rb +6 -9
- data/lib/ruby_llm/contract/concerns/stub_helpers.rb +97 -0
- data/lib/ruby_llm/contract/contract/definition.rb +2 -0
- data/lib/ruby_llm/contract/cost_calculator.rb +11 -2
- data/lib/ruby_llm/contract/eval/recommender.rb +3 -1
- data/lib/ruby_llm/contract/eval/retry_optimizer.rb +16 -13
- data/lib/ruby_llm/contract/eval.rb +13 -0
- data/lib/ruby_llm/contract/minitest.rb +6 -108
- data/lib/ruby_llm/contract/pipeline/result.rb +1 -1
- data/lib/ruby_llm/contract/rake_task/suite_gate.rb +117 -0
- data/lib/ruby_llm/contract/rake_task.rb +30 -51
- data/lib/ruby_llm/contract/rspec/helpers.rb +9 -123
- data/lib/ruby_llm/contract/step/base.rb +56 -24
- data/lib/ruby_llm/contract/step/dsl.rb +91 -63
- data/lib/ruby_llm/contract/step/limit_checker.rb +34 -1
- data/lib/ruby_llm/contract/step/retry_executor.rb +6 -13
- data/lib/ruby_llm/contract/step/runner.rb +22 -20
- data/lib/ruby_llm/contract/step/runner_config.rb +26 -0
- data/lib/ruby_llm/contract/version.rb +1 -1
- data/lib/ruby_llm/contract.rb +1 -0
- data/ruby_llm-contract.gemspec +5 -1
- metadata +3 -4
- data/.rspec +0 -3
- data/.rubycritic.yml +0 -8
- data/.simplecov +0 -22
|
@@ -6,6 +6,37 @@ module RubyLLM
|
|
|
6
6
|
# Extracted from Base to reduce class length.
|
|
7
7
|
# DSL accessor methods for step definition (input_type, output_type, prompt, etc.).
|
|
8
8
|
module Dsl # rubocop:disable Metrics/ModuleLength
|
|
9
|
+
# Sentinel signalling "explicitly reset" (`some_attr(:default)`).
|
|
10
|
+
# Distinguishes reset (lookup stops at this class, returns nil) from
|
|
11
|
+
# "never set" (lookup falls through to superclass).
|
|
12
|
+
UNSET = Object.new
|
|
13
|
+
def UNSET.inspect = "Step::Dsl::UNSET"
|
|
14
|
+
UNSET.freeze
|
|
15
|
+
|
|
16
|
+
# Walks the inheritance chain for a class-level DSL attribute.
|
|
17
|
+
# Returns the first explicitly-set value found, or nil.
|
|
18
|
+
def inherited_value(name)
|
|
19
|
+
ivar = :"@#{name}"
|
|
20
|
+
return instance_variable_get(ivar) if instance_variable_defined?(ivar)
|
|
21
|
+
|
|
22
|
+
superclass.public_send(name) if superclass.respond_to?(name)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Like `inherited_value`, but honours the `UNSET` sentinel — when this
|
|
26
|
+
# class has been reset via `some_attr(:default)`, returns nil without
|
|
27
|
+
# falling through to the superclass.
|
|
28
|
+
def inherited_value_with_reset(name)
|
|
29
|
+
ivar = :"@#{name}"
|
|
30
|
+
if instance_variable_defined?(ivar)
|
|
31
|
+
value = instance_variable_get(ivar)
|
|
32
|
+
return value unless value.equal?(UNSET)
|
|
33
|
+
|
|
34
|
+
return nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
superclass.public_send(name) if superclass.respond_to?(name)
|
|
38
|
+
end
|
|
39
|
+
|
|
9
40
|
def input_type(type = nil)
|
|
10
41
|
return @input_type = type if type
|
|
11
42
|
|
|
@@ -70,6 +101,11 @@ module RubyLLM
|
|
|
70
101
|
end
|
|
71
102
|
|
|
72
103
|
def validate(description, &block)
|
|
104
|
+
# `nil.to_s.empty?` is already true - the explicit nil branch was
|
|
105
|
+
# redundant. `caller` pushes the backtrace to user code instead
|
|
106
|
+
# of DSL internals (per Codex review of 0.10.0).
|
|
107
|
+
raise ArgumentError, "validate description must be a non-empty string", caller if description.to_s.empty?
|
|
108
|
+
|
|
73
109
|
(@class_validates ||= []) << Invariant.new(description, block)
|
|
74
110
|
end
|
|
75
111
|
|
|
@@ -91,131 +127,114 @@ module RubyLLM
|
|
|
91
127
|
|
|
92
128
|
def max_output(tokens = nil)
|
|
93
129
|
if tokens
|
|
94
|
-
|
|
95
|
-
raise ArgumentError, "max_output must be positive, got #{tokens}"
|
|
96
|
-
end
|
|
97
|
-
|
|
130
|
+
validate_positive!("max_output", tokens)
|
|
98
131
|
return @max_output = tokens
|
|
99
132
|
end
|
|
100
133
|
|
|
101
|
-
|
|
102
|
-
@max_output
|
|
103
|
-
elsif superclass.respond_to?(:max_output)
|
|
104
|
-
superclass.max_output
|
|
105
|
-
end
|
|
134
|
+
inherited_value(:max_output)
|
|
106
135
|
end
|
|
107
136
|
|
|
108
137
|
def max_input(tokens = nil)
|
|
109
138
|
if tokens
|
|
110
|
-
|
|
111
|
-
raise ArgumentError, "max_input must be positive, got #{tokens}"
|
|
112
|
-
end
|
|
113
|
-
|
|
139
|
+
validate_positive!("max_input", tokens)
|
|
114
140
|
return @max_input = tokens
|
|
115
141
|
end
|
|
116
142
|
|
|
117
|
-
|
|
118
|
-
@max_input
|
|
119
|
-
elsif superclass.respond_to?(:max_input)
|
|
120
|
-
superclass.max_input
|
|
121
|
-
end
|
|
143
|
+
inherited_value(:max_input)
|
|
122
144
|
end
|
|
123
145
|
|
|
124
146
|
def max_cost(amount = nil, on_unknown_pricing: nil)
|
|
125
147
|
if amount == :default
|
|
126
|
-
@max_cost =
|
|
127
|
-
@max_cost_explicitly_unset = true
|
|
148
|
+
@max_cost = UNSET
|
|
128
149
|
@on_unknown_pricing = nil
|
|
129
150
|
return nil
|
|
130
151
|
end
|
|
131
152
|
|
|
132
153
|
if amount
|
|
133
|
-
|
|
134
|
-
raise ArgumentError, "max_cost must be positive, got #{amount}"
|
|
135
|
-
end
|
|
154
|
+
validate_positive!("max_cost", amount)
|
|
136
155
|
|
|
137
156
|
if on_unknown_pricing && !%i[refuse warn].include?(on_unknown_pricing)
|
|
138
157
|
raise ArgumentError, "on_unknown_pricing must be :refuse or :warn, got #{on_unknown_pricing.inspect}"
|
|
139
158
|
end
|
|
140
159
|
|
|
141
|
-
@max_cost_explicitly_unset = false
|
|
142
160
|
@max_cost = amount
|
|
143
161
|
@on_unknown_pricing = on_unknown_pricing || :refuse
|
|
144
162
|
return @max_cost
|
|
145
163
|
end
|
|
146
164
|
|
|
147
|
-
|
|
148
|
-
return nil if @max_cost_explicitly_unset
|
|
149
|
-
|
|
150
|
-
superclass.max_cost if superclass.respond_to?(:max_cost)
|
|
165
|
+
inherited_value_with_reset(:max_cost)
|
|
151
166
|
end
|
|
152
167
|
|
|
153
168
|
def on_unknown_pricing
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
169
|
+
inherited_value(:on_unknown_pricing) || :refuse
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def attachment_token_estimate(n = nil)
|
|
173
|
+
if n == :default
|
|
174
|
+
@attachment_token_estimate = UNSET
|
|
175
|
+
return nil
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
if n
|
|
179
|
+
validate_positive!("attachment_token_estimate", n)
|
|
180
|
+
return @attachment_token_estimate = n
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
inherited_value_with_reset(:attachment_token_estimate)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def on_unknown_attachment_size(mode = nil)
|
|
187
|
+
if mode
|
|
188
|
+
unless %i[refuse warn].include?(mode)
|
|
189
|
+
raise ArgumentError,
|
|
190
|
+
"on_unknown_attachment_size must be :refuse or :warn, got #{mode.inspect}"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
return @on_unknown_attachment_size = mode
|
|
160
194
|
end
|
|
195
|
+
|
|
196
|
+
inherited_value(:on_unknown_attachment_size) || :refuse
|
|
161
197
|
end
|
|
162
198
|
|
|
163
199
|
def model(name = nil)
|
|
164
200
|
if name == :default
|
|
165
|
-
@model =
|
|
166
|
-
@model_explicitly_unset = true
|
|
201
|
+
@model = UNSET
|
|
167
202
|
return nil
|
|
168
203
|
end
|
|
169
204
|
|
|
170
|
-
if name
|
|
171
|
-
@model_explicitly_unset = false
|
|
172
|
-
return @model = name
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
return @model if defined?(@model) && !@model_explicitly_unset
|
|
176
|
-
return nil if @model_explicitly_unset
|
|
205
|
+
return @model = name if name
|
|
177
206
|
|
|
178
|
-
|
|
207
|
+
inherited_value_with_reset(:model)
|
|
179
208
|
end
|
|
180
209
|
|
|
181
210
|
def temperature(value = nil)
|
|
182
211
|
if value == :default
|
|
183
|
-
@temperature =
|
|
184
|
-
@temperature_explicitly_unset = true
|
|
212
|
+
@temperature = UNSET
|
|
185
213
|
return nil
|
|
186
214
|
end
|
|
187
215
|
|
|
188
|
-
|
|
216
|
+
# NOTE: `value` may be 0 (a legitimate setting); use `nil?` rather
|
|
217
|
+
# than truthiness to distinguish "no arg passed" from "explicit 0".
|
|
218
|
+
unless value.nil?
|
|
189
219
|
unless value.is_a?(Numeric) && value >= 0 && value <= 2
|
|
190
220
|
raise ArgumentError, "temperature must be 0.0-2.0, got #{value}"
|
|
191
221
|
end
|
|
192
222
|
|
|
193
|
-
@temperature_explicitly_unset = false
|
|
194
223
|
return @temperature = value
|
|
195
224
|
end
|
|
196
225
|
|
|
197
|
-
|
|
198
|
-
return nil if @temperature_explicitly_unset
|
|
199
|
-
|
|
200
|
-
superclass.temperature if superclass.respond_to?(:temperature)
|
|
226
|
+
inherited_value_with_reset(:temperature)
|
|
201
227
|
end
|
|
202
228
|
|
|
203
229
|
def thinking(effort: nil, budget: nil)
|
|
204
230
|
if effort == :default
|
|
205
|
-
@thinking =
|
|
206
|
-
@thinking_explicitly_unset = true
|
|
231
|
+
@thinking = UNSET
|
|
207
232
|
return nil
|
|
208
233
|
end
|
|
209
234
|
|
|
210
|
-
if effort || budget
|
|
211
|
-
@thinking_explicitly_unset = false
|
|
212
|
-
return @thinking = { effort: effort, budget: budget }.compact
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
return @thinking if defined?(@thinking) && !@thinking_explicitly_unset
|
|
216
|
-
return nil if @thinking_explicitly_unset
|
|
235
|
+
return @thinking = { effort: effort, budget: budget }.compact if effort || budget
|
|
217
236
|
|
|
218
|
-
|
|
237
|
+
inherited_value_with_reset(:thinking)
|
|
219
238
|
end
|
|
220
239
|
|
|
221
240
|
def reasoning_effort(value = nil)
|
|
@@ -228,7 +247,6 @@ module RubyLLM
|
|
|
228
247
|
if value == :default
|
|
229
248
|
current_budget = thinking && thinking[:budget]
|
|
230
249
|
if current_budget
|
|
231
|
-
@thinking_explicitly_unset = false
|
|
232
250
|
@thinking = { budget: current_budget }
|
|
233
251
|
return nil
|
|
234
252
|
end
|
|
@@ -261,6 +279,16 @@ module RubyLLM
|
|
|
261
279
|
superclass.retry_policy
|
|
262
280
|
end
|
|
263
281
|
end
|
|
282
|
+
|
|
283
|
+
private
|
|
284
|
+
|
|
285
|
+
# Shared positivity guard for `max_input`, `max_output`, `max_cost`,
|
|
286
|
+
# `attachment_token_estimate`. Mirrors `CostCalculator.validate_price!`.
|
|
287
|
+
def validate_positive!(name, value)
|
|
288
|
+
return if value.is_a?(Numeric) && value.positive?
|
|
289
|
+
|
|
290
|
+
raise ArgumentError, "#{name} must be positive, got #{value}"
|
|
291
|
+
end
|
|
264
292
|
end
|
|
265
293
|
end
|
|
266
294
|
end
|
|
@@ -11,7 +11,13 @@ module RubyLLM
|
|
|
11
11
|
def check_limits(messages)
|
|
12
12
|
return nil unless max_input || max_cost
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
text_tokens = TokenEstimator.estimate(messages)
|
|
15
|
+
attachment_tokens, attachment_error = resolve_attachment_tokens
|
|
16
|
+
if attachment_error
|
|
17
|
+
return build_limit_result(messages, text_tokens, [attachment_error])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
estimated = text_tokens + attachment_tokens
|
|
15
21
|
errors = collect_limit_errors(estimated)
|
|
16
22
|
|
|
17
23
|
return nil if errors.empty?
|
|
@@ -19,6 +25,33 @@ module RubyLLM
|
|
|
19
25
|
build_limit_result(messages, estimated, errors)
|
|
20
26
|
end
|
|
21
27
|
|
|
28
|
+
# Fail-closed: when an attachment is passed via context but no
|
|
29
|
+
# attachment_token_estimate is declared, the gem cannot bound vision/
|
|
30
|
+
# PDF cost. Refuses with a clear error unless on_unknown_attachment_size
|
|
31
|
+
# is :warn (per-step opt-out, mirroring on_unknown_pricing).
|
|
32
|
+
def resolve_attachment_tokens
|
|
33
|
+
return [0, nil] unless attachment_present?
|
|
34
|
+
|
|
35
|
+
estimate = attachment_token_estimate
|
|
36
|
+
if estimate.nil?
|
|
37
|
+
if on_unknown_attachment_size == :warn
|
|
38
|
+
warn "[ruby_llm-contract] attachment present but " \
|
|
39
|
+
"attachment_token_estimate not declared — cost limit not enforced " \
|
|
40
|
+
"for the attachment portion"
|
|
41
|
+
return [0, nil]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
return [0,
|
|
45
|
+
"attachment present but attachment_token_estimate not declared; " \
|
|
46
|
+
"cost cannot be bounded. Declare " \
|
|
47
|
+
"`attachment_token_estimate(n)` on the step class, or set " \
|
|
48
|
+
"`on_unknown_attachment_size :warn` to proceed without attachment " \
|
|
49
|
+
"cost checks."]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
[estimate, nil]
|
|
53
|
+
end
|
|
54
|
+
|
|
22
55
|
def collect_limit_errors(estimated)
|
|
23
56
|
errors = []
|
|
24
57
|
if max_input && estimated > max_input
|
|
@@ -6,6 +6,8 @@ module RubyLLM
|
|
|
6
6
|
# Extracted from Base to reduce class length.
|
|
7
7
|
# Handles retry logic: run_with_retry, build_retry_result, aggregate usage, build attempt entries.
|
|
8
8
|
module RetryExecutor
|
|
9
|
+
include Concerns::UsageAggregator
|
|
10
|
+
|
|
9
11
|
private
|
|
10
12
|
|
|
11
13
|
def run_with_retry(input, adapter:, default_model:, policy:, context_temperature: nil, extra_options: {})
|
|
@@ -29,7 +31,7 @@ module RubyLLM
|
|
|
29
31
|
def build_retry_result(all_attempts)
|
|
30
32
|
last = all_attempts.last[:result]
|
|
31
33
|
attempt_log = all_attempts.map { |attempt| build_attempt_entry(attempt) }
|
|
32
|
-
aggregated_usage =
|
|
34
|
+
aggregated_usage = aggregate_usage(all_attempts.map { |a| a[:result].trace })
|
|
33
35
|
total_cost = sum_attempt_costs(all_attempts)
|
|
34
36
|
total_latency = sum_attempt_latency(all_attempts)
|
|
35
37
|
|
|
@@ -80,18 +82,9 @@ module RubyLLM
|
|
|
80
82
|
end
|
|
81
83
|
end
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
usage = attempt[:result].trace
|
|
87
|
-
usage = usage.respond_to?(:usage) ? usage.usage : nil
|
|
88
|
-
next unless usage.is_a?(Hash)
|
|
89
|
-
|
|
90
|
-
totals[:input_tokens] += usage[:input_tokens] || 0
|
|
91
|
-
totals[:output_tokens] += usage[:output_tokens] || 0
|
|
92
|
-
end
|
|
93
|
-
totals
|
|
94
|
-
end
|
|
85
|
+
# `aggregate_usage(traces)` provided by Concerns::UsageAggregator —
|
|
86
|
+
# replaces the prior `aggregate_retry_usage` private helper which
|
|
87
|
+
# duplicated the same per-trace input/output token sum.
|
|
95
88
|
end
|
|
96
89
|
end
|
|
97
90
|
end
|
|
@@ -6,26 +6,15 @@ module RubyLLM
|
|
|
6
6
|
class Runner
|
|
7
7
|
include LimitChecker
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
adapter: adapter,
|
|
19
|
-
model: model,
|
|
20
|
-
output_schema: output_schema,
|
|
21
|
-
max_output: max_output,
|
|
22
|
-
max_input: max_input,
|
|
23
|
-
max_cost: max_cost,
|
|
24
|
-
on_unknown_pricing: on_unknown_pricing,
|
|
25
|
-
temperature: temperature,
|
|
26
|
-
extra_options: extra_options,
|
|
27
|
-
observers: observers
|
|
28
|
-
)
|
|
9
|
+
# Two construction forms:
|
|
10
|
+
# Runner.new(config: a_runner_config) # preferred — value-object
|
|
11
|
+
# Runner.new(input_type:, output_type:, ...) # legacy kwarg form (still supported)
|
|
12
|
+
#
|
|
13
|
+
# The legacy form delegates to `RunnerConfig.build(**kwargs)`, so the
|
|
14
|
+
# defaults live in one place (`RunnerConfig.build`) and the kwarg
|
|
15
|
+
# surface is no longer duplicated here.
|
|
16
|
+
def initialize(config: nil, **kwargs)
|
|
17
|
+
@config = config || RunnerConfig.build(**kwargs)
|
|
29
18
|
end
|
|
30
19
|
|
|
31
20
|
def call(input)
|
|
@@ -90,6 +79,19 @@ module RubyLLM
|
|
|
90
79
|
@config.on_unknown_pricing
|
|
91
80
|
end
|
|
92
81
|
|
|
82
|
+
def attachment_token_estimate
|
|
83
|
+
@config.attachment_token_estimate
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def on_unknown_attachment_size
|
|
87
|
+
@config.on_unknown_attachment_size
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def attachment_present?
|
|
91
|
+
opts = @config.extra_options
|
|
92
|
+
!opts.nil? && !opts[:attachment].nil?
|
|
93
|
+
end
|
|
94
|
+
|
|
93
95
|
def effective_max_output
|
|
94
96
|
@config.effective_max_output
|
|
95
97
|
end
|
|
@@ -15,10 +15,36 @@ module RubyLLM
|
|
|
15
15
|
:max_input,
|
|
16
16
|
:max_cost,
|
|
17
17
|
:on_unknown_pricing,
|
|
18
|
+
:attachment_token_estimate,
|
|
19
|
+
:on_unknown_attachment_size,
|
|
18
20
|
:temperature,
|
|
19
21
|
:extra_options,
|
|
20
22
|
:observers
|
|
21
23
|
) do
|
|
24
|
+
# Factory with sensible defaults for optional fields. Lets callers
|
|
25
|
+
# (Step::Base#run_once and tests) construct a RunnerConfig without
|
|
26
|
+
# repeating the 11-default boilerplate, and gives `Runner.new(config:)`
|
|
27
|
+
# a clean entry point for the value-object form.
|
|
28
|
+
def self.build(input_type:, output_type:, prompt_block:, contract_definition:,
|
|
29
|
+
adapter:, model:,
|
|
30
|
+
output_schema: nil, max_output: nil,
|
|
31
|
+
max_input: nil, max_cost: nil, on_unknown_pricing: :refuse,
|
|
32
|
+
attachment_token_estimate: nil, on_unknown_attachment_size: :refuse,
|
|
33
|
+
temperature: nil, extra_options: {}, observers: [])
|
|
34
|
+
new(
|
|
35
|
+
input_type: input_type, output_type: output_type,
|
|
36
|
+
prompt_block: prompt_block, contract_definition: contract_definition,
|
|
37
|
+
adapter: adapter, model: model,
|
|
38
|
+
output_schema: output_schema, max_output: max_output,
|
|
39
|
+
max_input: max_input, max_cost: max_cost,
|
|
40
|
+
on_unknown_pricing: on_unknown_pricing,
|
|
41
|
+
attachment_token_estimate: attachment_token_estimate,
|
|
42
|
+
on_unknown_attachment_size: on_unknown_attachment_size,
|
|
43
|
+
temperature: temperature, extra_options: extra_options,
|
|
44
|
+
observers: observers
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
22
48
|
def effective_max_output
|
|
23
49
|
extra_options[:max_tokens] || max_output
|
|
24
50
|
end
|
data/lib/ruby_llm/contract.rb
CHANGED
|
@@ -158,6 +158,7 @@ require_relative "contract/concerns/production_mode_context"
|
|
|
158
158
|
require_relative "contract/concerns/eval_host"
|
|
159
159
|
require_relative "contract/concerns/trace_equality"
|
|
160
160
|
require_relative "contract/concerns/usage_aggregator"
|
|
161
|
+
require_relative "contract/concerns/stub_helpers"
|
|
161
162
|
require_relative "contract/configuration"
|
|
162
163
|
require_relative "contract/prompt/node"
|
|
163
164
|
require_relative "contract/prompt/nodes"
|
data/ruby_llm-contract.gemspec
CHANGED
|
@@ -23,9 +23,13 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
24
24
|
|
|
25
25
|
spec.files = Dir.chdir(__dir__) do
|
|
26
|
+
# Internal trackers + dev configs excluded so the published gem
|
|
27
|
+
# contains only what adopters actually need at runtime.
|
|
28
|
+
excluded_files = %w[TODO.md .rspec .rubycritic.yml .simplecov]
|
|
26
29
|
`git ls-files -z`.split("\x0").reject do |f|
|
|
27
30
|
(File.expand_path(f) == __FILE__) ||
|
|
28
|
-
f.start_with?("spec/", "docs/", "doc/", ".ai/", ".claude/", ".git")
|
|
31
|
+
f.start_with?("spec/", "docs/", "doc/", ".ai/", ".claude/", ".git", ".revive/") ||
|
|
32
|
+
excluded_files.include?(f)
|
|
29
33
|
end
|
|
30
34
|
end
|
|
31
35
|
spec.require_paths = ["lib"]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_llm-contract
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justyna
|
|
@@ -59,10 +59,7 @@ executables: []
|
|
|
59
59
|
extensions: []
|
|
60
60
|
extra_rdoc_files: []
|
|
61
61
|
files:
|
|
62
|
-
- ".rspec"
|
|
63
62
|
- ".rubocop.yml"
|
|
64
|
-
- ".rubycritic.yml"
|
|
65
|
-
- ".simplecov"
|
|
66
63
|
- CHANGELOG.md
|
|
67
64
|
- Gemfile
|
|
68
65
|
- Gemfile.lock
|
|
@@ -88,6 +85,7 @@ files:
|
|
|
88
85
|
- lib/ruby_llm/contract/concerns/deep_symbolize.rb
|
|
89
86
|
- lib/ruby_llm/contract/concerns/eval_host.rb
|
|
90
87
|
- lib/ruby_llm/contract/concerns/production_mode_context.rb
|
|
88
|
+
- lib/ruby_llm/contract/concerns/stub_helpers.rb
|
|
91
89
|
- lib/ruby_llm/contract/concerns/trace_equality.rb
|
|
92
90
|
- lib/ruby_llm/contract/concerns/usage_aggregator.rb
|
|
93
91
|
- lib/ruby_llm/contract/configuration.rb
|
|
@@ -159,6 +157,7 @@ files:
|
|
|
159
157
|
- lib/ruby_llm/contract/prompt/renderer.rb
|
|
160
158
|
- lib/ruby_llm/contract/railtie.rb
|
|
161
159
|
- lib/ruby_llm/contract/rake_task.rb
|
|
160
|
+
- lib/ruby_llm/contract/rake_task/suite_gate.rb
|
|
162
161
|
- lib/ruby_llm/contract/rspec.rb
|
|
163
162
|
- lib/ruby_llm/contract/rspec/helpers.rb
|
|
164
163
|
- lib/ruby_llm/contract/rspec/pass_eval.rb
|
data/.rspec
DELETED
data/.rubycritic.yml
DELETED
data/.simplecov
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "simplecov"
|
|
4
|
-
|
|
5
|
-
SimpleCov.start do
|
|
6
|
-
enable_coverage :branch
|
|
7
|
-
primary_coverage :branch
|
|
8
|
-
|
|
9
|
-
add_filter "/spec/"
|
|
10
|
-
add_filter "/examples/"
|
|
11
|
-
add_filter "/internal/"
|
|
12
|
-
add_filter "/tmp/"
|
|
13
|
-
|
|
14
|
-
track_files "lib/**/*.rb"
|
|
15
|
-
|
|
16
|
-
if ENV["CI"] == "true" || ENV["SIMPLECOV_STRICT"] == "1"
|
|
17
|
-
minimum_coverage line: 89
|
|
18
|
-
minimum_coverage branch: 75
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
command_name "RSpec"
|
|
22
|
-
end
|