Openai
OpenAI
Bases: LLM
OpenAI wrapper that supports Structured Outputs + (optional) reasoning summaries via Responses API.
Source code in src/kibad_llm/llms/openai.py
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | |
get_reasoning_from_chat_response(response)
Return the OpenAI Responses API reasoning summary (not raw CoT).
Source code in src/kibad_llm/llms/openai.py
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | |
make_openai_strict_json_schema(schema)
Convert a JSON Schema (often generated by Pydantic) into a form that is accepted by OpenAI Structured Outputs when using the Responses API with
text={"format": {"type": "json_schema", "strict": True, "schema": ...}}
(or the equivalent response_format shape in other clients).
Why this exists
OpenAI Structured Outputs validates schemas using a restricted subset of JSON Schema
plus additional strict-mode constraints. A schema produced by
BaseModel.model_json_schema() is typically valid JSON Schema, but can still be rejected
by OpenAI for reasons such as:
1) Object strictness:
- Every object schema must forbid undeclared keys via additionalProperties: false.
- Every object schema must provide required, and in strict mode OpenAI expects
required to include every key in properties (even if fields have defaults).
Pydantic usually omits defaulted fields from required (e.g., default_factory=list).
2) $ref limitations:
OpenAI rejects schemas where a $ref appears alongside sibling keywords (e.g. a
property defined as {"$ref": "...", "description": "..."}), even though this is
permitted in full JSON Schema drafts. This commonly happens in Pydantic output when
a referenced definition is annotated with title/description.
3) Unsupported annotation keywords:
In strict mode, OpenAI rejects default in the schema. In JSON Schema, default
is an annotation (not used for validation), but OpenAI treats it as invalid input
for strict Structured Outputs.
What the function does
This helper walks the entire schema (including nested objects and $defs) and applies
the minimal transformations needed to satisfy OpenAI strict-mode requirements:
-
For every node that looks like an object schema (
{"type": "object", "properties": {...}}):- Set
required = list(properties.keys()) - Set
additionalProperties = false
- Set
-
Remove all
defaultkeys anywhere in the schema. -
For any dict node that contains
$refand other keys, rewrite it so that$refis placed inside ananyOf: {"$ref": "...", "description": "..."} -> {"anyOf": [{"$ref": "..."}], "description": "..."} This preserves the validation meaning (a single-entryanyOf) while avoiding the OpenAI restriction on$refsiblings.
Parameters
schema:
A JSON Schema dictionary (e.g., from BaseModel.model_json_schema()).
Returns
dict[str, Any] A patched schema dictionary. The returned schema is a deep copy of the input, so the original schema object is not mutated.
Notes
- The
required+additionalProperties: falsechanges do tighten validation compared to permissive JSON Schema, but match OpenAI strict-mode expectations and are usually aligned with "always emit all keys" extraction-style outputs. - If you want a field to be effectively optional while still being listed in
required, model it as nullable (e.g., viaanyOf: [{"type": "string"}, {"type": "null"}]) or use empty arrays as the "unknown" value for list fields. - This function does not guarantee that the resulting schema is accepted by every guided-decoding backend (e.g., vLLM/outlines/xgrammar). Consider keeping a separate "raw" Pydantic schema for self-hosted decoding and using this transformer only for OpenAI strict Structured Outputs.
Example
raw = MyModel.model_json_schema(by_alias=False) strict_schema = make_openai_strict_json_schema(raw) request_kwargs["text"] = { ... "format": { ... "type": "json_schema", ... "name": "my_model", ... "strict": True, ... "schema": strict_schema, ... } ... }
Source code in src/kibad_llm/llms/openai.py
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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | |