2323 require_non_empty_str_field ,
2424 require_type ,
2525)
26+ from models .raw_access import (
27+ _optional_dict_absent_ok ,
28+ _optional_dict_default_empty ,
29+ _optional_list_absent_none ,
30+ _optional_list_absent_ok ,
31+ _optional_number_absent_ok ,
32+ )
2633
2734_logger = logging .getLogger (__name__ )
2835
@@ -146,61 +153,39 @@ def cursor_storage_payload(self) -> dict[str, Any]:
146153
147154 @property
148155 def newly_created_files (self ) -> list [Any ]:
149- value = self ._raw .get ("newlyCreatedFiles" )
150- if value is None :
151- return []
152- if not isinstance (value , list ):
153- _logger .warning (
154- "Schema drift in Composer %s: invalid type for newlyCreatedFiles (expected list, got %s)" ,
155- self .composer_id ,
156- type (value ).__name__ ,
157- )
158- return []
159- return value
156+ return _optional_list_absent_ok (
157+ self ._raw ,
158+ "newlyCreatedFiles" ,
159+ model = "Composer" ,
160+ entity_id = self .composer_id ,
161+ )
160162
161163 @property
162164 def code_block_data (self ) -> dict [str , Any ] | None :
163- value = self ._raw .get ("codeBlockData" )
164- if value is None :
165- return None
166- if not isinstance (value , dict ):
167- _logger .warning (
168- "Schema drift in Composer %s: invalid type for codeBlockData (expected dict, got %s)" ,
169- self .composer_id ,
170- type (value ).__name__ ,
171- )
172- return None
173- return value
165+ return _optional_dict_absent_ok (
166+ self ._raw ,
167+ "codeBlockData" ,
168+ model = "Composer" ,
169+ entity_id = self .composer_id ,
170+ )
174171
175172 @property
176173 def usage_data (self ) -> dict [str , Any ]:
177174 """Composer cost rollup; empty dict when absent (common)."""
178- value = self ._raw .get ("usageData" )
179- if value is None :
180- return {}
181- if not isinstance (value , dict ):
182- suffix = f" { self .composer_id } " if self .composer_id else ""
183- _logger .warning (
184- "Schema drift in Composer%s: invalid type for usageData (expected dict, got %s)" ,
185- suffix ,
186- type (value ).__name__ ,
187- )
188- return {}
189- return value
175+ return _optional_dict_default_empty (
176+ self ._raw ,
177+ "usageData" ,
178+ model = "Composer" ,
179+ entity_id = self .composer_id ,
180+ )
190181
191182 def _optional_counter (self , key : str ) -> int | float :
192- value = self ._raw .get (key , 0 )
193- if isinstance (value , bool ) or not isinstance (value , (int , float )):
194- if key in self ._raw :
195- suffix = f" { self .composer_id } " if self .composer_id else ""
196- _logger .warning (
197- "Schema drift in Composer%s: invalid type for %s (expected number, got %s)" ,
198- suffix ,
199- key ,
200- type (value ).__name__ ,
201- )
202- return 0
203- return cast (int | float , value )
183+ return _optional_number_absent_ok (
184+ self ._raw ,
185+ key ,
186+ model = "Composer" ,
187+ entity_id = self .composer_id ,
188+ )
204189
205190 @property
206191 def total_lines_added (self ) -> int | float :
@@ -307,114 +292,91 @@ def text(self) -> str | None:
307292
308293 @property
309294 def metadata (self ) -> BubbleMetadataDict :
310- value = self ._raw .get ("metadata" )
311- if value is None :
312- return {}
313- if not isinstance (value , dict ):
314- _logger .warning (
315- "Schema drift in Bubble %s: invalid type for metadata (expected dict, got %s)" ,
316- self .bubble_id ,
317- type (value ).__name__ ,
318- )
319- return {}
320- return cast (BubbleMetadataDict , value )
295+ return cast (
296+ BubbleMetadataDict ,
297+ _optional_dict_default_empty (
298+ self ._raw ,
299+ "metadata" ,
300+ model = "Bubble" ,
301+ entity_id = self .bubble_id ,
302+ ),
303+ )
321304
322305 @property
323306 def relevant_files (self ) -> list [str ]:
324- value = self ._raw .get ("relevantFiles" )
325- if value is None :
326- return []
327- if not isinstance (value , list ):
328- _logger .warning (
329- "Schema drift in Bubble %s: invalid type for relevantFiles (expected list, got %s)" ,
330- self .bubble_id ,
331- type (value ).__name__ ,
332- )
333- return []
334307 return _filter_str_list_elements (
335- value ,
308+ _optional_list_absent_ok (
309+ self ._raw ,
310+ "relevantFiles" ,
311+ model = "Bubble" ,
312+ entity_id = self .bubble_id ,
313+ ),
336314 model = "Bubble" ,
337315 record_id = self .bubble_id ,
338316 field = "relevantFiles" ,
339317 )
340318
341319 @property
342320 def attached_file_code_chunks_uris (self ) -> list [FileUriDict ]:
343- value = self ._raw .get ("attachedFileCodeChunksUris" )
344- if value is None :
345- return []
346- if not isinstance (value , list ):
347- _logger .warning (
348- "Schema drift in Bubble %s: invalid type for attachedFileCodeChunksUris (expected list, got %s)" ,
349- self .bubble_id ,
350- type (value ).__name__ ,
351- )
352- return []
353321 return _filter_dict_list_elements (
354- value ,
322+ _optional_list_absent_ok (
323+ self ._raw ,
324+ "attachedFileCodeChunksUris" ,
325+ model = "Bubble" ,
326+ entity_id = self .bubble_id ,
327+ ),
355328 model = "Bubble" ,
356329 record_id = self .bubble_id ,
357330 field = "attachedFileCodeChunksUris" ,
358331 )
359332
360333 @property
361334 def context (self ) -> BubbleContextDict :
362- value = self ._raw .get ("context" )
363- if value is None :
364- return {}
365- if not isinstance (value , dict ):
366- _logger .warning (
367- "Schema drift in Bubble %s: invalid type for context (expected dict, got %s)" ,
368- self .bubble_id ,
369- type (value ).__name__ ,
370- )
371- return {}
372- return cast (BubbleContextDict , value )
335+ return cast (
336+ BubbleContextDict ,
337+ _optional_dict_default_empty (
338+ self ._raw ,
339+ "context" ,
340+ model = "Bubble" ,
341+ entity_id = self .bubble_id ,
342+ ),
343+ )
373344
374345 @property
375346 def token_count (self ) -> TokenCountDict | None :
376- value = self ._raw .get ("tokenCount" )
377- if value is None :
378- return None
379- if not isinstance (value , dict ):
380- _logger .warning (
381- "Schema drift in Bubble %s: invalid type for tokenCount (expected dict, got %s)" ,
382- self .bubble_id ,
383- type (value ).__name__ ,
384- )
385- return None
386- return cast (TokenCountDict , value )
347+ value = _optional_dict_absent_ok (
348+ self ._raw ,
349+ "tokenCount" ,
350+ model = "Bubble" ,
351+ entity_id = self .bubble_id ,
352+ )
353+ return cast (TokenCountDict , value ) if value is not None else None
387354
388355 @property
389356 def tool_former_data (self ) -> ToolFormerDataDict | None :
390- value = self ._raw .get ("toolFormerData" )
391- if value is None :
392- return None
393- if not isinstance (value , dict ):
394- _logger .warning (
395- "Schema drift in Bubble %s: invalid type for toolFormerData (expected dict, got %s)" ,
396- self .bubble_id ,
397- type (value ).__name__ ,
398- )
399- return None
400- return cast (ToolFormerDataDict , value )
357+ value = _optional_dict_absent_ok (
358+ self ._raw ,
359+ "toolFormerData" ,
360+ model = "Bubble" ,
361+ entity_id = self .bubble_id ,
362+ )
363+ return cast (ToolFormerDataDict , value ) if value is not None else None
401364
402365 @property
403366 def model_info (self ) -> ModelInfoDict :
404- value = self ._raw .get ("modelInfo" )
405- if value is None :
406- return {}
407- if not isinstance (value , dict ):
408- _logger .warning (
409- "Schema drift in Bubble %s: invalid type for modelInfo (expected dict, got %s)" ,
410- self .bubble_id ,
411- type (value ).__name__ ,
412- )
413- return {}
414- return cast (ModelInfoDict , value )
367+ return cast (
368+ ModelInfoDict ,
369+ _optional_dict_default_empty (
370+ self ._raw ,
371+ "modelInfo" ,
372+ model = "Bubble" ,
373+ entity_id = self .bubble_id ,
374+ ),
375+ )
415376
416377 @property
417378 def thinking (self ) -> str | ThinkingDict | None :
379+ # Inline: accepts str | dict; no raw_access helper for that union.
418380 value = self ._raw .get ("thinking" )
419381 if value is None :
420382 return None
@@ -429,6 +391,7 @@ def thinking(self) -> str | ThinkingDict | None:
429391
430392 @property
431393 def thinking_duration_ms (self ) -> int | float | None :
394+ # Inline: absent -> None (not 0); bool guard before numeric check.
432395 value = self ._raw .get ("thinkingDurationMs" )
433396 if value is None :
434397 return None
@@ -443,31 +406,25 @@ def thinking_duration_ms(self) -> int | float | None:
443406
444407 @property
445408 def context_window_status_at_creation (self ) -> ContextWindowStatusDict :
446- value = self ._raw .get ("contextWindowStatusAtCreation" )
447- if value is None :
448- return {}
449- if not isinstance (value , dict ):
450- _logger .warning (
451- "Schema drift in Bubble %s: invalid type for contextWindowStatusAtCreation (expected dict, got %s)" ,
452- self .bubble_id ,
453- type (value ).__name__ ,
454- )
455- return {}
456- return cast (ContextWindowStatusDict , value )
409+ return cast (
410+ ContextWindowStatusDict ,
411+ _optional_dict_default_empty (
412+ self ._raw ,
413+ "contextWindowStatusAtCreation" ,
414+ model = "Bubble" ,
415+ entity_id = self .bubble_id ,
416+ ),
417+ )
457418
458419 @property
459420 def tool_results (self ) -> list [ToolResultEntry ] | None :
460- value = self ._raw .get ("toolResults" )
461- if value is None :
462- return None
463- if not isinstance (value , list ):
464- _logger .warning (
465- "Schema drift in Bubble %s: invalid type for toolResults (expected list, got %s)" ,
466- self .bubble_id ,
467- type (value ).__name__ ,
468- )
469- return None
470- return cast (list [ToolResultEntry ], value )
421+ value = _optional_list_absent_none (
422+ self ._raw ,
423+ "toolResults" ,
424+ model = "Bubble" ,
425+ entity_id = self .bubble_id ,
426+ )
427+ return cast (list [ToolResultEntry ], value ) if value is not None else None
471428
472429 def bubble_timestamp_ms (self ) -> int | float | None :
473430 """``createdAt`` or ``timestamp`` in milliseconds when present."""
0 commit comments