Commit Graph

7 Commits

Author SHA1 Message Date
dante01yoon
8d9ea888ae Remove unused threading import in metadata e2e test
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
Python Linting / Run Pylint (push) Waiting to run
Fixes ruff F401 caught by the PR's lint CI.
2026-05-19 23:24:02 +09:00
dante01yoon
85a12d0a83 Key metadata by token, not prompt_id, to survive id collisions
Adversarial review caught that a LIFO stack keyed by ``prompt_id`` still
mis-attributes events when queue execution order differs from registration
order: a second submission with the same ``prompt_id`` lands on top of the
stack, so the first prompt's events read the wrong workflow_id while it
runs, and the first's ``unregister`` then pops the second prompt's entry.

Replace the stack with an internal monotonic token. ``post_prompt``
registers metadata and stashes the returned token on ``extra_data`` under
``PROMPT_METADATA_TOKEN_KEY``. ``main.py``'s queue worker pulls the token
out, pins it on ``PromptServer.active_prompt_metadata_token`` for the
prompt's execution, and clears + unregisters in ``finally``. The merge in
``send_sync`` reads the active token, so each prompt's events are merged
with its own metadata regardless of ``prompt_id`` collisions.

- comfy_execution/metadata.py: ``merge_prompt_metadata`` now takes an
  active token; registry is ``dict[int, PromptMetadata]``; new
  ``PROMPT_METADATA_TOKEN_KEY`` constant for the extra_data carrier.
- server.py: ``register_prompt_metadata`` returns a token (or ``None``
  when no metadata applies); ``unregister`` takes a token;
  ``get_active_prompt_metadata`` snapshots the pinned entry.
- main.py: pops the token from extra_data, pins on the server, clears
  after the terminal "executing: {node: None}" send.
- execution.py ``PromptQueue``: wipe_queue / delete_queue_item now
  unregister by token extracted from each item's extra_data.
- comfy_execution/progress.py: reads workflow_id via
  ``get_active_prompt_metadata`` rather than per-prompt_id lookup.
- tests: unit tests updated for the token signature, plus a real E2E
  test (test_prompt_metadata_e2e.py) that instantiates the actual
  PromptServer and verifies same-prompt_id-different-workflow_id
  submissions don't cross-attribute.

Verified end-to-end against a live ComfyUI server: two submissions with
identical client-supplied prompt_id but different workflow_id each emit
their full execution event stream (execution_start, execution_cached,
executing, executed, execution_success, progress_state, terminal executing)
with the correct workflow_id top-level. 68 / 68 tests pass.
2026-05-19 22:19:15 +09:00
dante01yoon
b0c05af67f Address adversarial review: stack registry + tighten TEXT routing
Two issues raised against the per-prompt metadata registry:

1. Client-supplied prompt_id can collide (post_prompt accepts the id
   verbatim). With a flat dict-keyed registry, the second submission
   clobbered the first and a single unregister could erase metadata still
   needed by the other prompt. Now stored as a LIFO stack per prompt_id —
   most recent registration wins on merge, unregister pops one entry, the
   key is dropped only when the stack drains.

2. BinaryEventTypes.TEXT (send_progress_text) bypasses the metadata merge
   because the payload is bytes, and the wire format has no prompt_id /
   workflow_id field. The merge can't fix this without a wire-format
   change + frontend feature flag, which is out of scope for FE-745. Inside
   scope: default the sid to PromptServer.client_id so other clients no
   longer silently receive untagged text frames. Cross-tab isolation
   inside a single client still depends on the wire-format follow-up.

- comfy_execution/metadata.py: registry is dict[str, list[PromptMetadata]];
  merge_prompt_metadata reads stack[-1]; new resolve_progress_text_sid
  helper extracted so the routing default is unit-testable without the
  full server import chain.
- server.py: register_prompt_metadata appends to the stack;
  unregister_prompt_metadata pops; get_prompt_metadata returns a copy of
  the top entry; send_progress_text routes through resolve_progress_text_sid.
- tests: collision LIFO behavior, sid resolution default, and the existing
  merge tests updated to the stack shape. 16 new assertions in this file,
  104/104 pass overall.
2026-05-19 21:43:55 +09:00
dante01yoon
5396b4fe67 Propagate workflow_id via per-prompt metadata registry (FE-745)
PR #13684 added workflow_id directly to ~9 dict literals across execution.py,
progress.py and main.py, along with executor.workflow_id and
server.last_workflow_id state. It was reverted because the execution layer
should not know about workflow concepts and because a finally-clear race
emitted workflow_id=None on the terminal "executing" frame.

Instead, register per-prompt metadata on PromptServer at submission time
and merge it onto outbound WebSocket payloads inside send_sync. The merge
keys off prompt_id (already present on every execution event), so
execution.py stays workflow-agnostic. Metadata is unregistered in main.py's
queue loop AFTER the terminal executing send, which structurally removes
the race.

- New comfy_execution/metadata.py: PromptMetadata TypedDict +
  build_prompt_metadata + merge_prompt_metadata helpers.
- PromptServer: prompt_metadata registry (lock-protected), register on
  post_prompt, merge in send_sync, expose get_prompt_metadata.
- jobs.py: extracted extract_workflow_id with strict isinstance guards;
  _extract_job_metadata delegates.
- main.py: try/finally around the queue iteration; unregister after the
  terminal "executing: {node: None}" send.
- execution.py PromptQueue: drop registry entries on wipe_queue /
  delete_queue_item so cancellations don't leak.
- progress.py: look up workflow_id from the server registry for the
  per-node nested copies and the binary preview metadata, matching #13684's
  wire shape so the frontend needs no changes.
- Tests: tests-unit/server_test/test_prompt_metadata.py covers the merge,
  the passthrough cases (no prompt_id, unknown prompt_id, binary payloads),
  and the terminal-frame race regression.
2026-05-19 17:16:11 +09:00
Christian Byrne
e0982a7174
fix: use no-store cache headers to prevent stale frontend chunks (#12911)
After a frontend update (e.g. nightly build), browsers could load
outdated cached index.html and JS/CSS chunks, causing dynamically
imported modules to fail with MIME type errors and vite:preloadError.

Hard refresh (Ctrl+Shift+R) was insufficient to fix the issue because
Cache-Control: no-cache still allows the browser to cache and
revalidate via ETags. aiohttp's FileResponse auto-generates ETags
based on file mtime+size, which may not change after pip reinstall,
so the browser gets 304 Not Modified and serves stale content.

Clearing ALL site data in DevTools did fix it, confirming the HTTP
cache was the root cause.

The fix changes:
- index.html: no-cache -> no-store, must-revalidate
- JS/CSS/JSON entry points: no-cache -> no-store

no-store instructs browsers to never cache these responses, ensuring
every page load fetches the current index.html with correct chunk
references. This is a small tradeoff (~5KB re-download per page load)
for guaranteed correctness after updates.
2026-03-14 18:25:09 -04:00
Christian Byrne
ed0f4a609b
dont cache new locale entry points (#10101) 2025-09-29 12:16:02 -07:00
Arjan Singh
3493b9cb1f
fix: add cache headers for images (#9560) 2025-09-05 14:32:25 -04:00