Skip to content

Commit 850e8b4

Browse files
feat: add text preview support to jobs API (Comfy-Org#12169)
* feat: add text preview support to jobs API Amp-Thread-ID: https://ampcode.com/threads/T-019c0be0-9fc6-71ac-853a-7c7cc846b375 Co-authored-by: Amp <amp@ampcode.com> * test: update tests to expect text as previewable media type Amp-Thread-ID: https://ampcode.com/threads/T-019c0be0-9fc6-71ac-853a-7c7cc846b375 ---------
1 parent d159142 commit 850e8b4

File tree

2 files changed

+48
-11
lines changed

2 files changed

+48
-11
lines changed

comfy_execution/jobs.py

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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)
2626
THREE_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

7996
def _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

tests/execution/test_jobs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ class TestIsPreviewable:
3838
"""Unit tests for is_previewable()"""
3939

4040
def test_previewable_media_types(self):
41-
"""Images, video, audio, 3d media types should be previewable."""
42-
for media_type in ['images', 'video', 'audio', '3d']:
41+
"""Images, video, audio, 3d, text media types should be previewable."""
42+
for media_type in ['images', 'video', 'audio', '3d', 'text']:
4343
assert is_previewable(media_type, {}) is True
4444

4545
def test_non_previewable_media_types(self):
4646
"""Other media types should not be previewable."""
47-
for media_type in ['latents', 'text', 'metadata', 'files']:
47+
for media_type in ['latents', 'metadata', 'files']:
4848
assert is_previewable(media_type, {}) is False
4949

5050
def test_3d_extensions_previewable(self):

0 commit comments

Comments
 (0)