1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! OpenAI strict-mode JSON Schema enforcement.
//!
//! Why: OpenAI's structured-output (`response_format: json_schema`, `strict:
//! true`) mode — which OpenRouter forwards verbatim for `openai/*` models —
//! rejects any schema in which an `object` node either omits
//! `"additionalProperties": false` or fails to list every one of its
//! `properties` keys in `"required"`. Hand-written schemas reliably miss one
//! of these on nested objects (e.g. `findings.items`), producing
//! `Invalid schema for response_format 'review_output': ...
//! 'additionalProperties' is required to be supplied and to be false`, which
//! blocks EVERY OpenAI review. Bedrock/Anthropic and Gemini are lenient and
//! accept the stricter-but-valid form unchanged, so enforcing strict mode for
//! all providers is safe.
//!
//! What: [`enforce_strict_mode`] recursively walks a `serde_json::Value` JSON
//! Schema and, for every node typed `"object"` (or carrying a `properties`
//! map), sets `"additionalProperties": false` and rewrites `"required"` to
//! list every property key. It also descends into array `items`, nested
//! object `properties`, and the `additionalProperties` schema when it is
//! itself an object schema (rather than the boolean `false`).
//!
//! Test: see `schema_tests.rs` — covers the canonical review/verify shapes,
//! nested arrays-of-objects, idempotency, and the recursive invariant
//! (`assert_object_nodes_strict`).
use Value;
/// Recursively make a JSON Schema OpenAI strict-mode compliant in place.
///
/// Why: a single source of truth for strict-mode compliance means individual
/// schema builders (`review_response_schema`, `verify_response_schema`) never
/// have to hand-maintain `additionalProperties`/`required` on every nested
/// object — they declare the shape and call this once, eliminating the class
/// of bug where a nested object silently violates strict mode.
/// What: for every object node, sets `"additionalProperties": false` and makes
/// `"required"` contain exactly the keys present under `"properties"` (sorted
/// for deterministic output). Recurses into `properties` values, array
/// `items` (object or array of schemas), and a schema-valued
/// `additionalProperties`. Non-object values are returned unchanged.
/// Test: `enforces_additional_properties_false`, `marks_all_properties_required`,
/// `recurses_into_array_items`, `is_idempotent` in `schema_tests.rs`.
// Re-export the recursive strict-mode assertion so the prompt-schema tests in
// `pipeline::prompt_tests` reuse the single canonical implementation instead of
// duplicating it. `pub(crate)` keeps it test-only (the `tests` module is
// `#[cfg(test)]`) and crate-private.
pub use assert_object_nodes_strict;