openaivec 0.14.8__py3-none-any.whl → 0.14.10__py3-none-any.whl

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.
openaivec/_dynamic.py ADDED
@@ -0,0 +1,350 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from enum import Enum
5
+ from typing import Literal
6
+
7
+ from pydantic import BaseModel, Field, create_model
8
+
9
+ __all__: list[str] = []
10
+
11
+ _MAX_ENUM_VALUES = 24
12
+
13
+
14
+ class FieldSpec(BaseModel):
15
+ name: str = Field(
16
+ description=(
17
+ "Field name in lower_snake_case. Rules: (1) Use only lowercase letters, numbers, and underscores; "
18
+ "must start with a letter. (2) For numeric quantities append an explicit unit (e.g. 'duration_seconds', "
19
+ "'price_usd'). (3) Boolean fields use an affirmative 'is_' prefix (e.g. 'is_active'); avoid negative / "
20
+ "ambiguous forms like 'is_deleted' (prefer 'is_active', 'is_enabled'). (4) Name must be unique within the "
21
+ "containing object."
22
+ )
23
+ )
24
+ type: Literal[
25
+ "string",
26
+ "integer",
27
+ "float",
28
+ "boolean",
29
+ "enum",
30
+ "object",
31
+ "string_array",
32
+ "integer_array",
33
+ "float_array",
34
+ "boolean_array",
35
+ "enum_array",
36
+ "object_array",
37
+ ] = Field(
38
+ description=(
39
+ "Logical data type. Allowed values: string | integer | float | boolean | enum | object | string_array | "
40
+ "integer_array | float_array | boolean_array | enum_array | object_array. *_array variants represent a "
41
+ "homogeneous list of the base type. 'enum' / 'enum_array' require 'enum_spec'. 'object' / 'object_array' "
42
+ "require 'object_spec'. Primitives must not define 'enum_spec' or 'object_spec'."
43
+ )
44
+ )
45
+ description: str = Field(
46
+ description=(
47
+ "Human‑readable, concise explanation of the field's meaning and business intent. Should clarify units, "
48
+ "value semantics, and any domain constraints not captured by type. 1–2 sentences; no implementation notes."
49
+ )
50
+ )
51
+ enum_spec: EnumSpec | None = Field(
52
+ default=None,
53
+ description=(
54
+ "Enumeration specification for 'enum' / 'enum_array'. Must be provided (non-empty) for those types and "
55
+ "omitted for all others. Maximum size enforced by constant."
56
+ ),
57
+ )
58
+ object_spec: ObjectSpec | None = Field(
59
+ default=None,
60
+ description=(
61
+ "Nested object schema. Required for 'object' / 'object_array'; must be omitted for every other type. The "
62
+ "contained 'name' is used to derive the generated nested Pydantic model class name."
63
+ ),
64
+ )
65
+
66
+
67
+ class EnumSpec(BaseModel):
68
+ """Enumeration specification for enum / enum_array field types.
69
+
70
+ Attributes:
71
+ name: Required Enum class name (UpperCamelCase). Must match ^[A-Z][A-Za-z0-9]*$. Previously optional; now
72
+ explicit to remove implicit coupling to the field name and make schemas self‑describing.
73
+ values: Raw label values (1–_MAX_ENUM_VALUES before de‑dup). Values are uppercased then
74
+ de-duplicated using a set; ordering of generated Enum members is not guaranteed. Any
75
+ casing variants collapse silently to a single member.
76
+ """
77
+
78
+ name: str = Field(
79
+ description=("Required Enum class name (UpperCamelCase). Valid pattern: ^[A-Z][A-Za-z0-9]*$."),
80
+ )
81
+ values: list[str] = Field(
82
+ description=(
83
+ f"Raw enum label values (1–{_MAX_ENUM_VALUES}). Uppercased then deduplicated; order of members "
84
+ "not guaranteed."
85
+ )
86
+ )
87
+
88
+
89
+ class ObjectSpec(BaseModel):
90
+ name: str = Field(
91
+ description=(
92
+ "Object model class name in UpperCamelCase (singular noun). Must match ^[A-Z][A-Za-z0-9]*$ and is used "
93
+ "directly as the generated Pydantic model class name (no transformation)."
94
+ )
95
+ )
96
+ fields: list[FieldSpec] = Field(
97
+ description=(
98
+ "Non-empty list of FieldSpec definitions composing the object. Each field name must be unique; order is "
99
+ "preserved in the generated model."
100
+ )
101
+ )
102
+
103
+
104
+ def _build_model(object_spec: ObjectSpec) -> type[BaseModel]:
105
+ lower_sname_pattern = re.compile(r"^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$")
106
+ upper_camel_pattern = re.compile(r"^[A-Z][A-Za-z0-9]*$")
107
+ type_map: dict[str, type] = {
108
+ "string": str,
109
+ "integer": int,
110
+ "float": float,
111
+ "boolean": bool,
112
+ "string_array": list[str],
113
+ "integer_array": list[int],
114
+ "float_array": list[float],
115
+ "boolean_array": list[bool],
116
+ }
117
+ output_fields: dict[str, tuple[type, object]] = {}
118
+
119
+ field_names: list[str] = [field.name for field in object_spec.fields]
120
+
121
+ # Assert that names of fields are not duplicated
122
+ if len(field_names) != len(set(field_names)):
123
+ raise ValueError("Field names must be unique within the object spec.")
124
+
125
+ for field in object_spec.fields:
126
+ # Assert that field names are lower_snake_case
127
+ if not lower_sname_pattern.match(field.name):
128
+ raise ValueError(f"Field name '{field.name}' must be in lower_snake_case format (e.g., 'my_field_name').")
129
+
130
+ # (EnumSpec.name now mandatory; no need to derive a fallback name from the field.)
131
+ match field:
132
+ case FieldSpec(
133
+ name=name,
134
+ type="string"
135
+ | "integer"
136
+ | "float"
137
+ | "boolean"
138
+ | "string_array"
139
+ | "integer_array"
140
+ | "float_array"
141
+ | "boolean_array",
142
+ description=description,
143
+ enum_spec=None,
144
+ object_spec=None,
145
+ ):
146
+ field_type = type_map[field.type]
147
+ output_fields[name] = (field_type, Field(description=description))
148
+
149
+ case FieldSpec(name=name, type="enum", description=description, enum_spec=enum_spec, object_spec=None) if (
150
+ enum_spec
151
+ and 0 < len(enum_spec.values) <= _MAX_ENUM_VALUES
152
+ and upper_camel_pattern.match(enum_spec.name)
153
+ ):
154
+ member_names = list({v.upper() for v in enum_spec.values})
155
+ enum_type = Enum(enum_spec.name, member_names)
156
+ output_fields[name] = (enum_type, Field(description=description))
157
+
158
+ case FieldSpec(
159
+ name=name, type="enum_array", description=description, enum_spec=enum_spec, object_spec=None
160
+ ) if (
161
+ enum_spec
162
+ and 0 < len(enum_spec.values) <= _MAX_ENUM_VALUES
163
+ and upper_camel_pattern.match(enum_spec.name)
164
+ ):
165
+ member_names = list({v.upper() for v in enum_spec.values})
166
+ enum_type = Enum(enum_spec.name, member_names)
167
+ output_fields[name] = (list[enum_type], Field(description=description))
168
+
169
+ case FieldSpec(
170
+ name=name, type="object", description=description, enum_spec=None, object_spec=object_spec
171
+ ) if object_spec and upper_camel_pattern.match(object_spec.name):
172
+ nested_model = _build_model(object_spec)
173
+ output_fields[name] = (nested_model, Field(description=description))
174
+
175
+ case FieldSpec(
176
+ name=name, type="object_array", description=description, enum_spec=None, object_spec=object_spec
177
+ ) if object_spec and upper_camel_pattern.match(object_spec.name):
178
+ nested_model = _build_model(object_spec)
179
+ output_fields[name] = (list[nested_model], Field(description=description))
180
+
181
+ # ---- Error cases (explicit reasons) ----
182
+ # Enum type without enum_spec (None or empty)
183
+ case FieldSpec(
184
+ name=name,
185
+ type="enum",
186
+ enum_spec=enum_spec,
187
+ object_spec=None,
188
+ ) if not enum_spec or not enum_spec.values:
189
+ raise ValueError(f"Field '{name}': enum type requires non-empty enum_spec values list.")
190
+ # Enum type exceeding max length
191
+ case FieldSpec(
192
+ name=name,
193
+ type="enum",
194
+ enum_spec=enum_spec,
195
+ object_spec=None,
196
+ ) if enum_spec and len(enum_spec.values) > _MAX_ENUM_VALUES:
197
+ raise ValueError(
198
+ (
199
+ f"Field '{name}': enum type supports at most {_MAX_ENUM_VALUES} enum_spec values "
200
+ f"(got {len(enum_spec.values)})."
201
+ )
202
+ )
203
+ # Enum type invalid explicit name pattern
204
+ case FieldSpec(
205
+ name=name,
206
+ type="enum",
207
+ enum_spec=enum_spec,
208
+ object_spec=None,
209
+ ) if enum_spec and not upper_camel_pattern.match(enum_spec.name):
210
+ raise ValueError(
211
+ (f"Field '{name}': enum_spec.name '{enum_spec.name}' invalid – must match ^[A-Z][A-Za-z0-9]*$")
212
+ )
213
+ # Enum type incorrectly provides an object_spec
214
+ case FieldSpec(
215
+ name=name,
216
+ type="enum",
217
+ enum_spec=enum_spec,
218
+ object_spec=object_spec,
219
+ ) if object_spec is not None:
220
+ raise ValueError(
221
+ f"Field '{name}': enum type must not provide object_spec (got object_spec={object_spec!r})."
222
+ )
223
+ # Enum array type without enum_spec
224
+ case FieldSpec(
225
+ name=name,
226
+ type="enum_array",
227
+ enum_spec=enum_spec,
228
+ object_spec=None,
229
+ ) if not enum_spec or not enum_spec.values:
230
+ raise ValueError(f"Field '{name}': enum_array type requires non-empty enum_spec values list.")
231
+ # Enum array type exceeding max length
232
+ case FieldSpec(
233
+ name=name,
234
+ type="enum_array",
235
+ enum_spec=enum_spec,
236
+ object_spec=None,
237
+ ) if enum_spec and len(enum_spec.values) > _MAX_ENUM_VALUES:
238
+ raise ValueError(
239
+ (
240
+ f"Field '{name}': enum_array type supports at most {_MAX_ENUM_VALUES} enum_spec values "
241
+ f"(got {len(enum_spec.values)})."
242
+ )
243
+ )
244
+ # Enum array type invalid explicit name pattern
245
+ case FieldSpec(
246
+ name=name,
247
+ type="enum_array",
248
+ enum_spec=enum_spec,
249
+ object_spec=None,
250
+ ) if enum_spec and not upper_camel_pattern.match(enum_spec.name):
251
+ raise ValueError(
252
+ (f"Field '{name}': enum_spec.name '{enum_spec.name}' invalid – must match ^[A-Z][A-Za-z0-9]*$")
253
+ )
254
+ # Enum array type incorrectly provides an object_spec
255
+ case FieldSpec(
256
+ name=name,
257
+ type="enum_array",
258
+ enum_spec=enum_spec,
259
+ object_spec=object_spec,
260
+ ) if object_spec is not None:
261
+ raise ValueError(
262
+ f"Field '{name}': enum_array type must not provide object_spec (got object_spec={object_spec!r})."
263
+ )
264
+ # Object type missing object_spec
265
+ case FieldSpec(
266
+ name=name,
267
+ type="object",
268
+ enum_spec=enum_spec,
269
+ object_spec=None,
270
+ ):
271
+ raise ValueError(f"Field '{name}': object type requires object_spec (got object_spec=None).")
272
+ # Object array type missing object_spec
273
+ case FieldSpec(
274
+ name=name,
275
+ type="object_array",
276
+ enum_spec=enum_spec,
277
+ object_spec=None,
278
+ ):
279
+ raise ValueError(f"Field '{name}': object_array type requires object_spec (got object_spec=None).")
280
+ # Object/object_array provided but invalid name pattern
281
+ case FieldSpec(
282
+ name=name,
283
+ type="object" | "object_array",
284
+ enum_spec=enum_spec,
285
+ object_spec=object_spec,
286
+ ) if object_spec is not None and not upper_camel_pattern.match(object_spec.name):
287
+ raise ValueError(
288
+ (
289
+ f"Field '{name}': object_spec.name '{object_spec.name}' must be UpperCamelCase "
290
+ "(regex ^[A-Z][A-Za-z0-9]*$) and contain only letters and digits."
291
+ )
292
+ )
293
+ # Object/object_array types must not provide enum_spec
294
+ case FieldSpec(
295
+ name=name,
296
+ type="object" | "object_array",
297
+ enum_spec=enum_spec,
298
+ object_spec=object_spec,
299
+ ) if enum_spec is not None:
300
+ raise ValueError(
301
+ f"Field '{name}': {field.type} must not define enum_spec (got enum_spec={enum_spec!r})."
302
+ )
303
+ # Primitive / simple array types must not have enum_spec
304
+ case FieldSpec(
305
+ name=name,
306
+ type="string"
307
+ | "integer"
308
+ | "float"
309
+ | "boolean"
310
+ | "string_array"
311
+ | "integer_array"
312
+ | "float_array"
313
+ | "boolean_array",
314
+ enum_spec=enum_spec,
315
+ object_spec=object_spec,
316
+ ) if enum_spec is not None:
317
+ raise ValueError(
318
+ (f"Field '{name}': type '{field.type}' must not define enum_spec (got enum_spec={enum_spec!r}).")
319
+ )
320
+ # Primitive / simple array types must not have object_spec
321
+ case FieldSpec(
322
+ name=name,
323
+ type="string"
324
+ | "integer"
325
+ | "float"
326
+ | "boolean"
327
+ | "string_array"
328
+ | "integer_array"
329
+ | "float_array"
330
+ | "boolean_array",
331
+ enum_spec=None,
332
+ object_spec=object_spec,
333
+ ) if object_spec is not None:
334
+ raise ValueError(
335
+ (
336
+ f"Field '{name}': type '{field.type}' must not define object_spec "
337
+ f"(got object_spec={object_spec!r})."
338
+ )
339
+ )
340
+ # Any other unmatched combination
341
+ case FieldSpec() as f:
342
+ raise ValueError(
343
+ (
344
+ "Field configuration invalid / unrecognized combination: "
345
+ f"name={f.name!r}, type={f.type!r}, enum_spec={'set' if f.enum_spec else None}, "
346
+ f"object_spec={'set' if f.object_spec else None}."
347
+ )
348
+ )
349
+
350
+ return create_model(object_spec.name, **output_fields)