mirror of
https://github.com/Comfy-Org/ComfyUI.git
synced 2026-08-25 18:32:35 +08:00
feat(jobs): cursor-based pagination on GET /api/jobs (BE-943)
Mirror the cloud jobs cursor (BE-885) on the OSS Python server so the
frontend sees one contract across runtimes.
- apply_sorting now appends the job id as a tiebreaker, making (create_time,
id) a stable keyset; without it, ties could reorder between pages.
- get_all_jobs accepts an opaque 'after' cursor (honored only for created_at
sort, like cloud), keyset-filters the sorted in-memory list, and returns
has_more + a next_cursor. Minted in offset mode too so a client can bootstrap
into keyset pagination.
- server.py /api/jobs parses 'after', returns next_cursor in the pagination
object, and maps a malformed cursor to 400 INVALID_CURSOR.
- Reuses the shared utils.cursor codec (base64url JSON {s,v,id,o}) so the wire
format matches cloud and assets exactly.
Tests: asc/desc multi-page round-trip, same-create_time tiebreaker, last-page
no-cursor, offset-mode bootstrap, execution_duration ignores cursor, malformed
cursor raises.
This commit is contained in:
45
server.py
45
server.py
@@ -9,6 +9,7 @@ import nodes
|
||||
import folder_paths
|
||||
import execution
|
||||
from comfy_execution.jobs import JobStatus, get_job, get_all_jobs
|
||||
from utils.cursor import InvalidCursorError
|
||||
import uuid
|
||||
import urllib
|
||||
import json
|
||||
@@ -785,6 +786,8 @@ class PromptServer():
|
||||
sort_order: Sort direction: asc, desc (default)
|
||||
limit: Max items to return (positive integer)
|
||||
offset: Items to skip (non-negative integer, default 0)
|
||||
after: Opaque keyset cursor from a prior next_cursor; takes
|
||||
precedence over offset. Honored only for created_at sort.
|
||||
"""
|
||||
query = request.rel_url.query
|
||||
|
||||
@@ -792,6 +795,7 @@ class PromptServer():
|
||||
workflow_id = query.get('workflow_id')
|
||||
sort_by = query.get('sort_by', 'created_at').lower()
|
||||
sort_order = query.get('sort_order', 'desc').lower()
|
||||
after = query.get('after')
|
||||
|
||||
status_filter = None
|
||||
if status_param:
|
||||
@@ -850,26 +854,35 @@ class PromptServer():
|
||||
running = _remove_sensitive_from_queue(running)
|
||||
queued = _remove_sensitive_from_queue(queued)
|
||||
|
||||
jobs, total = get_all_jobs(
|
||||
running, queued, history,
|
||||
status_filter=status_filter,
|
||||
workflow_id=workflow_id,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
limit=limit,
|
||||
offset=offset
|
||||
)
|
||||
try:
|
||||
jobs, total, has_more, next_cursor = get_all_jobs(
|
||||
running, queued, history,
|
||||
status_filter=status_filter,
|
||||
workflow_id=workflow_id,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
after=after
|
||||
)
|
||||
except InvalidCursorError:
|
||||
return web.json_response(
|
||||
{"error": "Invalid pagination cursor", "code": "INVALID_CURSOR"},
|
||||
status=400
|
||||
)
|
||||
|
||||
has_more = (offset + len(jobs)) < total
|
||||
pagination = {
|
||||
'offset': offset,
|
||||
'limit': limit,
|
||||
'total': total,
|
||||
'has_more': has_more
|
||||
}
|
||||
if next_cursor is not None:
|
||||
pagination['next_cursor'] = next_cursor
|
||||
|
||||
return web.json_response({
|
||||
'jobs': jobs,
|
||||
'pagination': {
|
||||
'offset': offset,
|
||||
'limit': limit,
|
||||
'total': total,
|
||||
'has_more': has_more
|
||||
}
|
||||
'pagination': pagination
|
||||
})
|
||||
|
||||
@routes.get("/api/jobs/{job_id}")
|
||||
|
||||
Reference in New Issue
Block a user