Commit Graph
37 Commits
Author SHA1 Message Date
Vito Sansevero c001f94213 fix: diagnostics dependency check survives a watchdog without __version__
check_dependencies read watchdog.__version__ for its log line; recent
watchdog releases have no such attribute, so the AttributeError escaped and
took the whole diagnostics run down. Read it with a default.

Also covers utils/diagnostics.py: database and images-table checks against
a real temporary database, file system check, ComfyUI output detection
(folder_paths, configured directories, relative fallback, nothing found),
dependencies, the full run and the test image link.
2026-10-08 18:01:34 -07:00
Vito Sansevero 205cf153ea fix: image monitor recent-prompt fallback reads the paginated result
get_recent_prompts returns {"prompts": [...]} but _get_fallback_prompt
indexed it like a list, so the last-resort link to the most recent prompt
always failed silently. Accept both shapes.
2026-10-08 18:01:34 -07:00
Vito Sansevero 7d65ee5516 refactor: one prompt hash implementation
utils.hashing.generate_prompt_hash is now the only prompt hash:
PromptManagerBase._generate_hash delegates to it and the inline
hashlib.sha256 copies in the prompt tracker and image monitor are gone.

Remove PromptManagerBase._open_web_interface (it targeted a web_interface
folder that never existed) and the unused restart_gallery.py script.

IS_CHANGED on PromptManager and PromptManagerText now includes category and
tags, so editing either re-runs the node and updates the saved metadata.
2026-10-08 18:01:34 -07:00
Vito Sansevero 59ad23201b fix: image monitor validates directories before creating the observer
start_monitoring assigned self.observer before checking that any directory
existed, so with no valid directory the monitor was stuck "already running"
and stop_monitoring raised on join of a never-started observer. Directories
are validated first and only a started observer is kept; stop_monitoring is
safe to call at any time and always joins. New is_monitoring property.

The per-image threading.Timer is replaced by one daemon worker: paths are
deduplicated and bounded (MAX_PENDING_IMAGES), processed in creation order
(batch images pop the prompt queue in order) after the processing delay and
once the file size is unchanged across two reads.
2026-10-08 18:01:34 -07:00
Vito Sansevero 5464e3295c fix: keep batch image queue aligned when unrelated workflows save images
The metadata strategy returned None for images whose embedded workflow has
no positive PromptManager node, so the handler popped the batch FIFO for
them and every later batch image linked to the wrong prompt. It now returns
a tri-state: linked (prompt found), foreign (no PromptManager prompt in the
workflow, so the image is skipped and no fallback applies) or unknown (only
then the queue, snapshot, tracker and recent-prompt fallbacks run).
2026-10-08 18:01:34 -07:00
Vito Sansevero 729190309c fix: count prompt runs when ComfyUI dequeues the prompt, not when it is submitted
The on-prompt hook runs before ComfyUI validates the request, so counting
there bumped run_count for prompts that were rejected or deleted from the
queue. The hook now only resolves the positive PromptManager texts, mints a
UUID4 prompt_id when the client sent none, and stashes the hashes under that
id (QueuedRuns, bounded to 1000 entries). PromptQueue.get is wrapped
idempotently; when a prompt is dequeued for execution its stashed runs are
counted (or remembered as pending first use). Servers without a prompt_queue
fall back to counting at submit time with one warning.

__init__ defines _global_db before the monitoring block and passes a factory
that raises RuntimeError while the database is unavailable; the package is
now imported under test with server and folder_paths stubbed.
2026-10-08 18:01:34 -07:00
Vito Sansevero 0a46420385 fix: do not follow negative text inputs when tracing the positive prompt
_TEXT_KEY matched input names such as text_negative, so a styler node feeding
a sampler's positive input pulled its negative PromptManager node into the
positive trace. Input names matching (^|_)neg(ative)?($|_) are now skipped
(new follows_input helper).
2026-10-08 18:01:34 -07:00
Vito Sansevero 248d5880e6 fix: never treat negative-only PromptManager nodes as the run prompt
run_prompt_nodes fell back to every PromptManager node whenever no positive
node was traced, including graphs that do have a sampler and whose only
PromptManager node feeds its negative input. Fall back only when the graph
has no recognisable sampler (new has_sampler helper). Samplers named like
one are recognised even without a model link. Adversarial graphs (cycles,
10k-node fan-out, non-dict inputs) are covered.
2026-10-08 18:01:34 -07:00
Vito Sansevero 2fdef18355 refactor: drop validate_workflow_name, the column it validated no longer exists 2026-10-08 18:01:34 -07:00
Vito 6c7e4d5b35 feat: release 3.2.4 — recently used sorting, run counts, positive-prompt image linking (#152)
* chore: start 3.2.4

* feat: track prompt usage (run_count, last_used_at) with server-side sorting

- Migration adds last_used_at and run_count; existing prompts are estimated
  from linked images (count, newest image time, else created_at). The
  backfill only touches NULL rows, so it self-heals rows written after a
  downgrade or an interrupted upgrade.
- Every node run records a use, including re-runs of an existing prompt;
  a tracking failure is logged and never blocks generation.
- search_prompts/get_recent_prompts accept a whitelisted sort key
  (last_used, created, run_count, rating, text); default stays newest first.

Refs #90

* feat: sort param on /recent and /search; microsecond last_used_at

- /prompt_manager/recent and /search accept ?sort= (whitelisted in the DB
  layer; unknown values fall back to newest first).
- last_used_at is written with microseconds from Python so a re-run in the
  same millisecond as another save still sorts first; ordering stays correct
  against millisecond values from the backfill.

Refs #90

* feat: server-side sort in admin UI with Recently Used / Most Used and run badges

- New web/js/prompt-list-sort.js owns the sort options, validation and
  request URLs; admin.js fills the dropdown from it and re-fetches from the
  server on change, so every sort spans all pages (the old client-side sort
  only reordered the current page).
- Default sort is Recently Used; prompt cards show a run-count badge with
  the last-used time on hover.
- The node's Recent button now lists recently used prompts.
- Contract test keeps the UI sort keys and the node's key in sync with the
  server whitelist.

Refs #90

* fix: link images and count runs by the positive prompt, not node execution

With a negative-prompt PromptManager node in the workflow, every executed
PromptManager node pushed onto the image-linking queue, so images could be
linked to the negative prompt or to a previous run. ComfyUI also skips
unchanged nodes, so re-runs of the same prompt were never counted.

- utils/prompt_graph.py finds the PromptManager nodes feeding a sampler's
  positive input (KSampler, CFG/Basic guiders, ControlNet and combine
  pass-throughs, PromptManagerText into encoders), falling back to all
  PromptManager nodes when none can be traced.
- Runs are counted by a ComfyUI on-prompt hook when a workflow is queued;
  prompts not yet saved are counted when the node first saves them.
- Nodes receive the queued graph (hidden PROMPT/UNIQUE_ID). Only the run's
  positive prompt becomes the current prompt; only batch items with linked
  text are pushed onto the FIFO queue.
- The image monitor links from the image's own metadata first, using the
  positive node, and falls back to the queue for batch items.

Refs #90

* fix: address review — count cached runs of linked text, keep repeated pending runs

- Text from known pure string nodes (core PrimitiveString/StringConcatenate,
  WAS Text Concatenate/Multiline, pysssss ShowText) is resolved at queue
  time, so cached re-runs of such prompts are counted and their images link
  from metadata. Unknown nodes are never guessed; the node compares its real
  text with the queue-time resolution and falls back to counting and
  queueing itself on any mismatch.
- A new prompt queued several times before its first run keeps every
  pending run; the first save records all of them.
- Registration flag is a threading.Event; consistent unittest imports;
  logging instead of print in the init fallback.

* refactor: unquote PendingFirstUse annotation so Tuple usage is visible
2026-10-07 13:52:09 -07:00
Vito aa768f57dd fix: release 3.2.3 — metadata parsing, null-safe saves, auto-hide, tag search (#151)
* fix: release 3.2.3 — metadata parsing, null-safe saves, auto-hide, tag search

- fix(#75): stop SaveImage patch from writing the last-run prompt into every
  PromptManager node (negative prompts were saved as copies of the positive)
- fix(#75): replace four duplicated metadata parsers with a shared
  web/js/comfy-metadata.js that traces sampler links (KSampler, CFGGuider /
  SamplerCustomAdvanced, LoRA chains, ControlNet pass-through, any checkpoint
  loader) and recovers per-node text for images saved by older versions
- fix(#143): treat null JSON body fields as empty in prompt API handlers
- fix(#149): keep auto-hide timeout 0 (disabled) instead of falling back to
  the default; validate result_timeout on save
- feat(#150): support tag:name filters in the node search box
- docs(#142): scan dialog mentions A1111/Forge metadata support
- docs(#144): fix ComfyUI-Lora-Manager links
- ci: run JS tests with node --test

* fix: bound metadata graph traversal to prevent tab freeze on crafted PNGs

Link resolution tried every linked input recursively with only a depth cap,
so a PNG whose prompt graph has cycles and fan-out could force ~inputs^25
paths and hang the gallery/metadata page. Each lookup now shares a visited
set (each node expanded once) with a hard cap of 500 visits.

* fix: read the matching output of multi-output settings nodes

KSampler settings linked to a node like SamplerCombo all resolved to its
first value, so steps, cfg and sampler showed the sampler name. Scalar
resolution now prefers the linked node's input with the same field name.

* fix: address review — trust API text, restore workflow-only parsing

- Workflow widget text now only replaces API text for metadata showing the
  pre-3.2.3 corruption signature (2+ PromptManager nodes with identical API
  text but different widgets). Otherwise the API graph wins, so expanded
  dynamic prompts and linked text display what actually ran.
- Images with only a UI workflow are converted to the API graph shape and
  traced the same way: prompts, KSamplerAdvanced, UNET/checkpoint loaders,
  KJNodes Set/Get, Reroute, bypassed and muted nodes.
2026-10-07 11:27:40 -07:00
Vito 8146250274 feat: PromptSearchList batch workflow support & image linking v3.2.2 (#139)
* fix(search): PromptSearchList crash, filters, and batch output (#52)

- Fix empty list crash (OUTPUT_IS_LIST requires at least one element)
- Add partial tag matching (LIKE instead of exact)
- Add preview and count outputs for batch visibility
- Add skip_multipart filter for Clip_N video prompts
- Add LoRA-only prompt filter
- Collapse newlines for StringOutputList compatibility

* feat(db): add tag_partial parameter for LIKE tag matching (#52)

Allows search_prompts to use LIKE matching for tags instead of exact.
Used by PromptSearchList for discovery-style search.
Existing callers default to exact matching (unchanged behavior).

* feat(tracker): add FIFO prompt queue for batch image linking (#52)

Batch workflows encode all prompts first, then save all images later.
The queue preserves encode order so each image links to the correct
prompt by position (pop on save matches push on encode).

* fix(monitor): multi-strategy image-to-prompt linking (#52)

Previous approach relied on live prompt tracker state, which was
always stale for batch workflows (all images linked to last prompt).

New 5-strategy priority chain:
1. FIFO queue pop (batch-correct by position)
2. Metadata-based DB lookup (extracts prompt from PNG)
3. Prompt snapshot at file-creation time
4. Live prompt tracker
5. Most recent DB prompt fallback

* test: add tests for prompt queue and search filters (#52)

- 6 queue tests: FIFO ordering, batch simulation, exhaustion
- 15 filter tests: newline collapse, Clip_ filter, LoRA-only filter
- Fix _make_tracker helper to include queue fields

* chore: bump version to 3.2.2

* fix: address code review — regex backtracking and queue expiry (#52)

- Replace backtracking-prone LoRA-only regex with subtraction approach
  (re.sub removes tags, check if anything remains). Fixes CodeQL alert.
- Add expiry check to pop_next_prompt — skips stale entries to prevent
  incorrect linking from accumulated queue entries across sessions.
- Cleanup thread now also prunes expired queue entries.
2026-04-10 12:49:59 -07:00
Vito 2cf9f42e73 fix: diagnostics and monitor respect config, singleton lifecycle (#132) (#133)
* fix: diagnostics, backup, and restore now respect config.json db path (#132)

* test: add regression tests for config-aware diagnostics and backup (#132)

* fix(test): use fresh event loop to avoid aiohttp test pollution

* fix: don't kill singleton image monitor when node instance is garbage collected

The __del__ method was calling stop_monitoring() on the shared singleton
ImageMonitor, killing it for ALL nodes whenever any single node instance
was garbage collected between workflow runs. This caused the monitor to
restart on every execution and miss image save events due to race conditions.

* test: add regression tests for image monitor singleton lifecycle

Ensures cleanup/del never stops the shared monitor, singleton is
thread-safe, and observer survives across node instance lifecycles.

* style: address PR review — log suppressed exceptions, remove unused imports
2026-04-03 06:22:55 -07:00
Vito Sansevero c9ca4be8b8 fix: custom image scan directory ignored due to wrong import (#108)
start_monitoring() used an absolute import (from py.config) to read
MONITORING_DIRECTORIES, which either failed with ImportError or loaded
a separate module instance with empty defaults. The configured custom
directory was never used, and auto-detection always kicked in.

Fixed by consolidating into the single relative import (from ..py.config)
that was already used for the MONITORING_ENABLED check.
2026-02-08 05:38:42 -08:00
Vito Sansevero 2737a4b1ef style: apply Black formatter (line-length=88) to all Python files
Automated formatting pass across 30 files to establish consistent code
style enforced by CI. No logic changes.
2026-02-07 08:30:09 -08:00
Vito Sansevero 4f62066c63 fix: harden nodes, validators, and utility modules
Phase 0-3 fixes from codebase audit:
- Extract shared node logic into prompt_manager_base.py (DRY)
- Add input validation guards to validators.py
- Fix logging_config.py buffer management
- Harden image_monitor.py against race conditions
- Fix prompt_tracker.py cleanup edge cases
- Fix diagnostics.py import path
- Wire config.py server instance correctly
2026-02-07 07:02:10 -08:00
Vito Sansevero 6ef040b4b8 refactor(comfyui): remove class_type conversion logic 2026-02-06 14:32:55 -08:00
Vito Sansevero f485825bfb fix: resolve gallery crash when thumbnails exist and prevent thumbnail-as-image linking
- Fix AttributeError: 'str' has no attribute 'as_posix' on thumbnail_rel_path
  in get_output_images and find_duplicates (thumbnail_rel_path is already a
  string from f-string construction, not a Path object)
- Fix NameError: total_images undefined in generate_thumbnails (was defined as
  total_media, causing progress logging and response to fail)
- Add URL encoding for paths in scan_output_dir for filenames with spaces
- Exclude thumbnails directory from image monitor to prevent linking thumbnail
  files to prompts via fallback mechanism
- Bump version to 3.0.31
2026-02-03 14:07:23 -08:00
Vito Sansevero a7d842a51c feat(settings): add configurable gallery scan directory
Add ability to configure a custom image scan directory in Settings:
- New "Image Scan Directory" field in Settings modal
- "Currently Monitoring" display shows active directory path
- Settings API now returns/saves gallery_root_path and monitored_directories
- Image monitor checks GalleryConfig before auto-detecting
- Restart notification when gallery path changes

Closes #76
2026-02-02 15:58:54 -08:00
Vito Sansevero c3aa41d679 feat: Add singleton management for PromptTracker 2026-02-02 15:09:58 -08:00
Vito Sansevero c50a7292c7 refactor(image_monitor): use info log level instead of debug 2026-02-02 15:09:48 -08:00
Vito Sansevero 22593e8514 docs: add comprehensive docstrings to all Python modules
- Add Google-style docstrings to main node files (prompt_manager.py, prompt_manager_text.py)
- Enhance database module docstrings with detailed parameter and return documentation
- Add comprehensive docstrings to all utils modules with usage examples
- Document API endpoints and configuration classes thoroughly
- Update module-level docstrings in all __init__.py files
- Ensure consistent naming (ComfyUI_PromptManager) across all documentation
- Follow Python best practices for docstring formatting
2025-08-12 06:51:36 -07:00
Vito Sansevero e2eef54eb3 feat(utils): Add ComfyUI integration utilities 2025-07-25 14:03:49 -07:00
Vito Sansevero 685e44fde0 style: Change log level from info to debug 2025-06-19 13:06:18 -07:00
Vito Sansevero ecbd2b4ca2 style: Change log level from info to debug 2025-06-19 13:06:09 -07:00
Vito Sansevero 299e74ebd5 style: Replace emojis with tags in diagnostics logs 2025-05-28 09:17:21 -07:00
Vito Sansevero 20d2483c3a fix(logging): add safe encoding for Windows compatibility 2025-05-28 09:17:09 -07:00
Vito Sansevero d255be3c99 refactor(diagnostics): Replace print with logger info 2025-05-28 08:28:26 -07:00
Vito Sansevero 1ea4ac7565 refactor(image_monitor): Replace print with logger 2025-05-28 08:28:18 -07:00
Vito Sansevero 367bc6c745 refactor(metadata_extractor): Replace print with logger 2025-05-28 08:28:09 -07:00
Vito Sansevero 226263495b feat(prompt_tracker): integrate logging system 2025-05-28 08:27:58 -07:00
Vito Sansevero c87d376c25 feat(logging): Add centralized logging configuration 2025-05-28 08:27:13 -07:00
Vito Sansevero fb11ce5013 refactor(prompt_tracker): Avoid duplicate prompt saving Fixes: #3 2025-05-27 12:36:28 -07:00
Vito Sansevero 8b399e2d3e refactor(prompt_tracker): add detailed logging for prompts, fixes #3 2025-05-27 11:10:31 -07:00
Vito Sansevero 777d5e6a6a feat(image_monitor): add fallback prompt handling 2025-05-27 10:59:01 -07:00
Vito Sansevero 2de1596568 refactor(prompt_tracker): increase prompt timeout to 10m 2025-05-27 10:58:46 -07:00
Vito Sansevero de1fe46563 feat(utils): add utility modules for diagnostics, hashing, image monitoring, metadata extraction, prompt tracking, and validation 2025-05-24 12:26:31 -07:00