ruby_llm-contract 0.10.2 → 0.10.3
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 +15 -0
- data/Gemfile.lock +2 -2
- data/README.md +1 -1
- data/lib/ruby_llm/contract/contract/schema_validator/object_rules.rb +24 -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: 4dc75c6a5ec293047bd2e2c480cf0aaff0fb3f5043f668a9291553a30002ed53
|
|
4
|
+
data.tar.gz: 4bd5960d8ff68b8708992755a95e8b265f28cdc3734db80dbdeada251eebfba9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aa5741f4728be169c42ee861aa6c8366481b21d49e2ff66105028d22d027ba7939959c784b13f7f8707ae0483608a0360cd035aee2ba4eadd6c2e4b30edfe680
|
|
7
|
+
data.tar.gz: 875ffe28d243189193d8dad0a4d2201329b92d9701f9eec49349719ac20d678b4de21a15db7db7f827fa19bb5d1adbdf22496186a17802147156cc88e1664a9d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.3 (2026-06-10)
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`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:
|
|
10
|
+
- `type: ["string", "null"]` (array form)
|
|
11
|
+
- `type: "null"` (degenerate scalar form)
|
|
12
|
+
- `anyOf: [{type: "string"}, {type: "null"}]` and `oneOf` equivalents
|
|
13
|
+
|
|
14
|
+
**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.
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
3
18
|
## 0.10.2 (2026-06-10)
|
|
4
19
|
|
|
5
20
|
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.3)
|
|
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.3)
|
|
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.3**). Semver tracked; breaking changes flagged in [CHANGELOG](CHANGELOG.md). Pin `~> 0.10.3` until 1.0 ships.
|
|
161
161
|
|
|
162
162
|
## FAQ
|
|
163
163
|
|
|
@@ -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
|
|