@@ -20,7 +20,7 @@ class JobStatus:
2020
2121
2222# Media types that can be previewed in the frontend
23- PREVIEWABLE_MEDIA_TYPES = frozenset ({'images' , 'video' , 'audio' , '3d' })
23+ PREVIEWABLE_MEDIA_TYPES = frozenset ({'images' , 'video' , 'audio' , '3d' , 'text' })
2424
2525# 3D file extensions for preview fallback (no dedicated media_type exists)
2626THREE_D_EXTENSIONS = frozenset ({'.obj' , '.fbx' , '.gltf' , '.glb' , '.usdz' })
@@ -75,6 +75,23 @@ def normalize_outputs(outputs: dict) -> dict:
7575 normalized [node_id ] = normalized_node
7676 return normalized
7777
78+ # Text preview truncation limit (1024 characters) to prevent preview_output bloat
79+ TEXT_PREVIEW_MAX_LENGTH = 1024
80+
81+
82+ def _create_text_preview (value : str ) -> dict :
83+ """Create a text preview dict with optional truncation.
84+
85+ Returns:
86+ dict with 'content' and optionally 'truncated' flag
87+ """
88+ if len (value ) <= TEXT_PREVIEW_MAX_LENGTH :
89+ return {'content' : value }
90+ return {
91+ 'content' : value [:TEXT_PREVIEW_MAX_LENGTH ],
92+ 'truncated' : True
93+ }
94+
7895
7996def _extract_job_metadata (extra_data : dict ) -> tuple [Optional [int ], Optional [str ]]:
8097 """Extract create_time and workflow_id from extra_data.
@@ -221,23 +238,43 @@ def get_outputs_summary(outputs: dict) -> tuple[int, Optional[dict]]:
221238 continue
222239
223240 for item in items :
224- normalized = normalize_output_item (item )
225- if normalized is None :
226- continue
241+ if not isinstance (item , dict ):
242+ # Handle text outputs (non-dict items like strings or tuples)
243+ normalized = normalize_output_item (item )
244+ if normalized is None :
245+ # Not a 3D file string — check for text preview
246+ if media_type == 'text' :
247+ count += 1
248+ if preview_output is None :
249+ if isinstance (item , tuple ):
250+ text_value = item [0 ] if item else ''
251+ else :
252+ text_value = str (item )
253+ text_preview = _create_text_preview (text_value )
254+ enriched = {
255+ ** text_preview ,
256+ 'nodeId' : node_id ,
257+ 'mediaType' : media_type
258+ }
259+ if fallback_preview is None :
260+ fallback_preview = enriched
261+ continue
262+ # normalize_output_item returned a dict (e.g. 3D file)
263+ item = normalized
227264
228265 count += 1
229266
230267 if preview_output is not None :
231268 continue
232269
233- if isinstance ( normalized , dict ) and is_previewable (media_type , normalized ):
270+ if is_previewable (media_type , item ):
234271 enriched = {
235- ** normalized ,
272+ ** item ,
236273 'nodeId' : node_id ,
237274 }
238- if 'mediaType' not in normalized :
275+ if 'mediaType' not in item :
239276 enriched ['mediaType' ] = media_type
240- if normalized .get ('type' ) == 'output' :
277+ if item .get ('type' ) == 'output' :
241278 preview_output = enriched
242279 elif fallback_preview is None :
243280 fallback_preview = enriched
0 commit comments