ruby_llm-contract 0.10.2 → 0.10.4
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 +24 -0
- data/Gemfile.lock +2 -2
- data/README.md +1 -1
- data/docs/guide/optimizing_retry_policy.md +1 -1
- data/lib/ruby_llm/contract/contract/schema_validator/object_rules.rb +24 -0
- data/lib/ruby_llm/contract/railtie.rb +8 -0
- data/lib/ruby_llm/contract/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7413f62a11d7d8b350d7ea24f5ef5df479c3f258d1c32e952553214a6182023e
|
|
4
|
+
data.tar.gz: 7061cdf57b6633130e20e1653ef5223c1948825afb95f878e50d0ac65b6995dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58af240032c98ff1d25bad9114c2ef83cbea54ab13e024e7b3665bf38b5c2478813dafec1a6666811539eaecc828d97c926010eb21d61f0daeb15de89c3d1ff2
|
|
7
|
+
data.tar.gz: 5b473df00607e1dbe9cb2d1e113fb72b9ab3fea534040eadb912aaee1d9d2d9ca1a2c99b2406fb0757124beae6fafecd03536aaf2c4e4df8d8072d8c38cfcbbb
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.4 (2026-06-10)
|
|
4
|
+
|
|
5
|
+
Patch release: the `ruby_llm_contract:optimize` rake task now auto-loads in Rails apps. No behaviour change to the task itself.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`ruby_llm_contract:optimize` no longer requires manual `require "ruby_llm/contract/rake_task"` in Rails apps.** Pre-0.10.4 the docs claimed the task was "included" with `RubyLLM::Contract::RakeTask`, but the railtie did not load the file — adopters running `bin/rails ruby_llm_contract:optimize` got `Unrecognized command` until they added the require to `Rakefile` or `lib/tasks/*.rake`. The railtie now uses the standard `rake_tasks { require "..." }` idiom, lazy-loading the file only when `rake` is invoked (no boot cost).
|
|
10
|
+
- **Docs corrected** in `docs/guide/optimizing_retry_policy.md` — the "rake task is included" line now explicitly states it auto-loads on 0.10.4+ in Rails, and that non-Rails / older-Rails setups still need the explicit `require`.
|
|
11
|
+
|
|
12
|
+
## 0.10.3 (2026-06-10)
|
|
13
|
+
|
|
14
|
+
Hot-fix release: the schema validator now correctly accepts `nil` on **required-but-nullable** fields. This unblocks OpenAI structured-output strict mode, where every property has to be in `required` and "nullable" is expressed as a `null` branch in `anyOf`/`oneOf` or as an array `type` — exactly the combination the prior validator wrongly rejected.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **`SchemaValidator` no longer rejects legal `nil` on required-nullable fields.** Pre-0.10.3 (every published version 0.2.x–0.10.2) conflated "required" (must be present) with "non-nullable" (cannot be null) — JSON Schema treats those as orthogonal. The validator now honours all three idioms for nullability:
|
|
19
|
+
- `type: ["string", "null"]` (array form)
|
|
20
|
+
- `type: "null"` (degenerate scalar form)
|
|
21
|
+
- `anyOf: [{type: "string"}, {type: "null"}]` and `oneOf` equivalents
|
|
22
|
+
|
|
23
|
+
**Adopter impact:** if you bypassed the bug by setting `required: false` + disabling OpenAI `strict: true` on every nullable field, you can now restore `strict: true` and keep the field required (= OpenAI's standard nullable idiom). Non-nullable required fields still reject `nil` exactly as before; this is a strictly additive fix.
|
|
24
|
+
|
|
25
|
+
Discovered via dogfooding in a production adopter using OpenAI strict structured output with 17 nullable fields (`"set the rest to null"` prompt). Smoking-gun mutation: remove `nullable_schema?` short-circuit from `validate_nil_field` — the three new spec cases in `spec/ruby_llm/contract/contract/schema_validator_spec.rb` fail.
|
|
26
|
+
|
|
3
27
|
## 0.10.2 (2026-06-10)
|
|
4
28
|
|
|
5
29
|
Patch release: ship the `docs/guide/` directory inside the gem so adopters and LLM integration agents can read the manuals locally (via `bundle show ruby_llm-contract` or `gem unpack`) without an internet round-trip to GitHub. No code behavior change.
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
ruby_llm-contract (0.10.
|
|
4
|
+
ruby_llm-contract (0.10.4)
|
|
5
5
|
dry-types (~> 1.7)
|
|
6
6
|
ruby_llm (~> 1.12)
|
|
7
7
|
ruby_llm-schema (~> 0.3)
|
|
@@ -258,7 +258,7 @@ CHECKSUMS
|
|
|
258
258
|
rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
|
|
259
259
|
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
260
260
|
ruby_llm (1.14.0) sha256=57c6f7034fc4a44504ea137d70f853b07824f1c1cdbe774ab3ab3522e7098deb
|
|
261
|
-
ruby_llm-contract (0.10.
|
|
261
|
+
ruby_llm-contract (0.10.4)
|
|
262
262
|
ruby_llm-schema (0.3.0) sha256=a591edc5ca1b7f0304f0e2261de61ba4b3bea17be09f5cf7558153adfda3dec6
|
|
263
263
|
ruby_parser (3.22.0) sha256=1eb4937cd9eb220aa2d194e352a24dba90aef00751e24c8dfffdb14000f15d23
|
|
264
264
|
rubycritic (4.12.0) sha256=024fed90fe656fa939f6ea80aab17569699ac3863d0b52fd72cb99892247abc8
|
data/README.md
CHANGED
|
@@ -157,7 +157,7 @@ Different layers, complementary. [`ruby_llm-tribunal`](https://github.com/Alqemi
|
|
|
157
157
|
|
|
158
158
|
## Status & versioning
|
|
159
159
|
|
|
160
|
-
Pre-1.0 (currently **0.10.
|
|
160
|
+
Pre-1.0 (currently **0.10.4**). Semver tracked; breaking changes flagged in [CHANGELOG](CHANGELOG.md). Pin `~> 0.10.4` until 1.0 ships.
|
|
161
161
|
|
|
162
162
|
## FAQ
|
|
163
163
|
|
|
@@ -8,7 +8,7 @@ You defined `SummarizeArticle` in the [README](../../README.md) with `retry_poli
|
|
|
8
8
|
|
|
9
9
|
- **`SummarizeArticle` already has `retry_policy`.** If your step has none, add one first ([getting started](getting_started.md)).
|
|
10
10
|
- **2–3 evals per step.** One eval optimizes for one scenario; with only `smoke`, you get a recommendation that passes smoke but may miss production edge cases. See [eval-first](eval_first.md).
|
|
11
|
-
- **Rake
|
|
11
|
+
- **Rake task `ruby_llm_contract:optimize` is registered when `lib/ruby_llm/contract/rake_task.rb` is loaded.** In Rails apps on **0.10.4+** the railtie auto-loads it lazily during `rake` invocation — no manual setup needed. On older Rails versions or in non-Rails projects, add `require "ruby_llm/contract/rake_task"` to your `Rakefile` (or any file under `lib/tasks/`) and set `EVAL_DIRS=...` on the command line.
|
|
12
12
|
|
|
13
13
|
> **Two orthogonal dimensions to a retry chain.** A chain element is `{ model:, reasoning_effort: }` — model identity AND thinking budget. `optimize_retry_policy` explores both. You can also fix the thinking config at class level via `thinking effort: :low` (or alias `reasoning_effort :low`) on the Step — it becomes the default for every chain element unless an override is passed. See the `thinking` DSL note at the bottom of this guide.
|
|
14
14
|
|
|
@@ -45,11 +45,35 @@ module RubyLLM
|
|
|
45
45
|
|
|
46
46
|
def validate_nil_field(path, field_schema, required)
|
|
47
47
|
return unless required
|
|
48
|
+
return if nullable_schema?(field_schema)
|
|
48
49
|
|
|
49
50
|
expected_type = field_schema[:type] || "non-null"
|
|
50
51
|
@errors << "#{path}: expected #{expected_type}, got nil"
|
|
51
52
|
end
|
|
52
53
|
|
|
54
|
+
# JSON Schema permits nil when the field schema explicitly accepts
|
|
55
|
+
# null. Three idiomatic forms are recognised:
|
|
56
|
+
# - `type: ["string", "null"]` (array form)
|
|
57
|
+
# - `type: "null"` (degenerate scalar form)
|
|
58
|
+
# - `anyOf` / `oneOf` containing a branch with `type: "null"`
|
|
59
|
+
# Without this gate the validator wrongly rejected legal nulls on
|
|
60
|
+
# required-but-nullable fields, forcing adopters into `required: false`
|
|
61
|
+
# + `strict: false` workarounds. Fixed in 0.10.3.
|
|
62
|
+
def nullable_schema?(field_schema)
|
|
63
|
+
return false unless field_schema.is_a?(Hash)
|
|
64
|
+
|
|
65
|
+
type = field_schema[:type] || field_schema["type"]
|
|
66
|
+
return true if type.is_a?(Array) && type.any? { |t| t.to_s == "null" }
|
|
67
|
+
return true if type.to_s == "null"
|
|
68
|
+
|
|
69
|
+
%i[anyOf oneOf].any? do |key|
|
|
70
|
+
branches = field_schema[key] || field_schema[key.to_s]
|
|
71
|
+
branches.is_a?(Array) && branches.any? do |branch|
|
|
72
|
+
branch.is_a?(Hash) && (branch[:type] || branch["type"]).to_s == "null"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
53
77
|
def validate_additional_properties(node)
|
|
54
78
|
return unless node.schema[:additionalProperties] == false
|
|
55
79
|
|
|
@@ -21,6 +21,14 @@ module RubyLLM
|
|
|
21
21
|
config.to_prepare do
|
|
22
22
|
RubyLLM::Contract.load_evals!
|
|
23
23
|
end
|
|
24
|
+
|
|
25
|
+
# Auto-load rake tasks in Rails apps so `bin/rails ruby_llm_contract:optimize`
|
|
26
|
+
# works without an explicit `require "ruby_llm/contract/rake_task"` in the
|
|
27
|
+
# adopter's Rakefile / lib/tasks. Standard Rails railtie idiom: the
|
|
28
|
+
# `rake_tasks` block fires lazily during `rake` invocation, no boot cost.
|
|
29
|
+
rake_tasks do
|
|
30
|
+
require "ruby_llm/contract/rake_task"
|
|
31
|
+
end
|
|
24
32
|
end
|
|
25
33
|
end
|
|
26
34
|
end
|