Commit Graph

2956 Commits

Author SHA1 Message Date
Will Miao 17dcbd3d4f fix(delete): merge delete batches manifest-only, never move files
Bulk delete merged staged batches by physically moving each loser's
files into the winner's batch dir with os.rename. Cross-volume bulks
(winner and loser on different filesystems) always hit EXDEV, forcing a
rollback and degrading to the batch_ids array with per-batch undo.

Merge is now manifest-only: loser entries are appended to the winner's
manifest with their staged paths unchanged, so staged files keep living
in each model's own .lm-pending-delete/<batch_id> dir (no data IO, no
EXDEV). Loser dirs are recorded in the winner manifest's merged_sources
and each loser manifest is stamped merged_into so its own purge timer, a
post-restart sweep or a direct undo call no-op. A cross-volume bulk is
one undoable batch again, and undo/purge clean up the loser dirs once
the merged batch settles.
2026-08-27 19:32:17 +08:00
Will Miao e914a0e19d fix(ui): reconcile model listing in place after download (#1078)
Stop resetting the whole listing after a successful download. The legacy
flow reloaded page 1, scrolled to the top and hijacked the sidebar's
active folder whenever a custom target folder was used, which made the
Updates view lose its place (and sometimes render as an empty page).

Downloads only flip the update flag for one model, so the listing is now
reconciled in place through the virtual scroller:

- Updates view: the model's cards are removed once its newest eligible
  version is installed (the flag is model-level).
- Normal listing: the card stays; only update_available is cleared.
- Model not in the current view (different folder/filter/window):
  no-op; the sidebar folder tree alone is refreshed.
- Falling back to the legacy reload only when no virtual scroller is
  available (e.g. recipes page, duplicates mode, HF downloads).
2026-08-27 18:41:57 +08:00
Will Miao 2ba04bb1bd docs: merge CLAUDE.md content into AGENTS.md and remove CLAUDE.md 2026-08-27 18:41:57 +08:00
Will Miao 1b7314591a docs(skill): streamline lora-manager-e2e and gate usage to true integration checks
- Add a 'when to use / when not to use' gate: UI behavior questions
  default to Vitest/jsdom, E2E only for behavior spanning server +
  browser; description updated so the skill triggers less eagerly
- Pin the browser driver to Chrome DevTools MCP and explain why
  kimi-webbridge (user's real browser) is not a substitute
- Drop generic MCP pattern boilerplate duplicated by
  references/mcp-cheatsheet.md (SKILL.md 385 -> 145 lines)
- Move recipe rematch fixture / fresh-state / cancel-gap notes to
  references/recipe-rematch-fixtures.md
2026-08-27 18:15:04 +08:00
Will Miao 2bfb987312 feat(models): add shared searchable base model picker and overhaul bulk base model modal
- Extract a shared BaseModelPicker (search, keyboard navigation,
  filename-based suggestions, dynamic API models such as MiniMax H3
  under 'Other (API)') used by both the single-model metadata modal
  and the bulk base model modal
- Rework the bulk base model modal into a dedicated inline-list
  layout: fixed modal size, sticky-free footer with app-standard
  modal-actions/primary-btn/cancel-btn buttons, and an inline option
  list that scrolls itself instead of an overlay dropdown covering
  the footer
- Selecting an option in change mode now filters the list to the
  selection instead of resetting and scroll-jumping to it
- Restore opaque sticky section headers in the bulk modal so scrolled
  items no longer bleed through
2026-08-27 18:07:28 +08:00
Will Miao df34efafbc feat(recipes): skip rate-limited batch-import items and register download 429s (#1085)
Phase 2 of docs/plans/issue-1085-rate-limit-design.md:

- Batch import: items that fail due to vendor rate limiting are now
  SKIPPED with a "re-run the import later" hint instead of FAILED, so a
  transient 429 no longer pollutes failure accounting; the progress
  broadcast carries a rate_limited flag.
- Batch import UI: show a one-time "rate limited — slowing down" toast
  and swap the running status text while rate_limited; i18n keys synced
  to all locales.
- Downloader: download_file / download_to_memory / get_response_headers
  register 429 cooldowns with the RateLimitCoordinator, so subsequent
  API calls queue behind a download-triggered rate-limit window.
2026-08-27 10:08:32 +08:00
Will Miao c2a2048c8b feat(services): add per-destination rate-limit gate for API traffic (#1085)
Implement Phase 1 of docs/plans/issue-1085-rate-limit-design.md:

- New RateLimitCoordinator: per-host shared Retry-After gate with
  exponential backoff (30s base, 1800s cap), minimum inter-request pacing
  (default 0.75s), herd-free waiter serialization via per-destination
  locks, and a bounded wait (default 300s) that raises instead of parking.
- Downloader.make_request: connectivity-guard fail-fast first, then gate
  pacing; on 429 register the cooldown and wait-and-resend (bounded);
  errors that passed through the gate are marked gate_handled.
- FallbackMetadataProvider / MetadataSyncService: a network provider 429
  no longer fails over to other network providers (stops the CivArchive
  flood); sqlite stays as local last resort. Rate-limited lookups now
  report "Rate limited" instead of "Model not found", so transient 429s
  no longer mark models civitai_deleted.
- _RateLimitRetryHelper skips its own sleep for gate_handled errors,
  removing the double wait.
- New settings: rate_limit_gate_enabled, rate_limit_max_wait_seconds,
  rate_limit_min_interval_seconds.
2026-08-27 09:53:07 +08:00
Will Miao 1e1921cabb docs(plans): rate-limit abidance design for recipe ingest (#1085) 2026-08-27 09:02:42 +08:00
Will Miao ee233548e5 fix(recipes): enforce batch-import concurrency bound and harden ingest errors (#1085)
Address the rate-limit flood and secondary errors seen during large
recipe ingestion (example-images directory import):

- batch import: share one adaptive-concurrency semaphore across the whole
  batch (previously each item got a fresh semaphore, so the min/max
  concurrency bounds never applied and every item ran concurrently);
  synchronize the shared semaphore capacity after each completed item.
- comfy parser: guard ckpt_name against list/None values so re.search no
  longer raises TypeError and fails the whole image import.
- civarchive client: normalize empty-string failure payloads to
  "Request failed" and treat a missing payload as an error, fixing the
  "'NoneType' object has no attribute 'get'" crash.
- civarchive client: log connectivity-guard offline-cooldown
  short-circuits at DEBUG instead of one ERROR per request.
2026-08-27 07:58:48 +08:00
Will Miao 574dfbbe55 feat(settings): add explicit settings dir override for sandboxed runs
Add LORA_MANAGER_SETTINGS_DIR env var and standalone --settings-path to pin
the settings location (settings.json, cache/, wildcards/, backups/, logs/,
stats/) to an arbitrary directory. The override takes precedence over
portable mode and the platform user config dir, and skips legacy migration,
so sandboxed dev/E2E runs no longer need to write settings.json in the repo
root or collide with the real instance.

standalone.py pre-scans argv for --settings-path at import time because the
settings location is resolved before main() parses arguments. SettingsManager
portable-switch migration is a no-op while the directory is pinned.

Update the lora-manager-e2e skill (prefer --settings-path sandboxing;
start_server.py passes it through) and the lora-manager-runtime-context
skill (document precedence; inspect script honors the override).
2026-08-27 00:03:50 +08:00
Will Miao 1d3bcdfe47 fix(skill): quote lora-manager-e2e description so YAML frontmatter parses 2026-08-26 22:56:54 +08:00
Will Miao 74369940bf fix(recipes): log batch import progress only when it changes (#1084) 2026-08-26 22:39:51 +08:00
Will Miao d188cec306 fix(recipes): restore batch import modal on reopen and log recipe ingest progress (#1084) 2026-08-26 22:32:20 +08:00
Will Miao 641a61f804 feat(relink): accept CivitArchive URLs when linking models 2026-08-26 21:31:30 +08:00
Will Miao 3025c64fea fix(recipes): serve duplicate scan from cache and guard against re-entry 2026-08-26 20:34:50 +08:00
Will Miao c52cfc7e7a fix(download): serialize concurrent downloads resolving to the same target path 2026-08-26 12:17:14 +08:00
Will Miao 4ed9f775f6 feat(bulk): add shift+click range selection in bulk mode 2026-08-26 10:09:35 +08:00
Will Miao 0b08ad283a fix(bulk): restore card selection when virtual scroller recreates cards 2026-08-26 09:28:17 +08:00
Will Miao 08895f77ff fix(update): align update-check summary count with Updates filter scope (#1083) 2026-08-25 20:36:46 +08:00
Will Miao 74f889f160 fix(recipes): validate FTS index from stored metadata instead of scanning 2026-08-25 17:38:31 +08:00
Will Miao c51090ab16 fix(recipes): make source_path backfill a one-shot migration 2026-08-25 17:38:17 +08:00
Will Miao cdb044cb45 fix(scanner): offload persisted-cache hydration from the event loop 2026-08-25 17:38:09 +08:00
Will Miao c83b26b556 fix(delete): run startup reconciliation walk off the event loop 2026-08-25 17:38:01 +08:00
Will Miao a202c666bc fix(download): return 200 for missing queue items and quiet download-progress 404s
The browser extension's apiFetch treats any 404 as a missing endpoint and
retries the legacy non-/api/lm URL, producing two spurious
'error_middleware - WARNING - API GET ... 404' log lines per occurrence.

- complete_download_in_queue / update_download_queue_status /
  retry_download_from_history: 'not found' is a normal business outcome,
  return 200 + success:false instead of 404 (extension behavior unchanged;
  apiGet ignores the HTTP status)
- error_middleware: downgrade /api/lm/download-progress/ 404s to debug like
  previews - the 404 status itself stays (extension uses it for failure
  detection), only the log level is lowered
2026-08-25 09:59:33 +08:00
Will Miao e05046af10 fix(loaders): sanitize invalid control_after_generate values when loading old workflows
Old workflows (saved before the control_after_generate feature) carry a
shorter widgets_values array. The frontend's index-based widget restore
then shifts the old weight_dtype value into the hidden control widget
(leaving an invalid value like 'default') and silently resets
weight_dtype to its default. On graph load, hand the shifted value back
to weight_dtype when it still sits at its default, then reset the
control mode to 'fixed' so old workflows keep loading deterministically.
2026-08-25 08:30:17 +08:00
Will Miao 41ed03e5c6 fix(download): stop stale aria2 GIDs from spamming errors after queue clears
- Log expected "GID not found" tellStatus probes at DEBUG, and treat a
  forgotten GID as permanent so the poll loop recovers immediately
  instead of burning 4 retries x 3s of ERROR lines per cycle
- cancel_download tolerates a forgotten GID and always pops the
  in-memory transfer so concurrent polls cannot re-register a
  cancelled download
- Restore sweep deletes aria2 state records with no resolvable target
  path instead of skipping them forever
- Clearing the download queue now also cancels in-memory tasks, removes
  live aria2 transfers and drops persisted state for the cleared ids
  (partial files on disk are preserved)
2026-08-25 08:19:54 +08:00
Will Miao da071e8452 feat(versions): add file-variant badge and hide download button for in-library versions (#1058) 2026-08-24 23:17:10 +08:00
Will Miao a0bb6df2b8 test(recipe): reset RecipeScanner singleton in lora availability fixture 2026-08-24 17:23:20 +08:00
Will Miao 6f5c444ec5 feat(recipes): add lora availability filter to recipe filter panel 2026-08-24 17:00:02 +08:00
Will Miao 20f66a4fe1 fix(ui): reload listing when an invalid folder selection falls back to root
After a drag move empties the selected folder, refresh() resets the
stale activeFolder to root but the grid kept showing the old filtered
(empty) view until a manual reload. Trigger resetAndReload when the
fallback happens post-initialization; the initial page load is untouched
because it picks up the cleared filter on its own.
2026-08-24 14:17:06 +08:00
Will Miao 879745da53 fix(init): add missing /api/lm/init-status endpoint used by polling fallback
initialization.js falls back to polling /api/lm/init-status when the
/ws/init-progress WebSocket cannot be established, but no route ever
registered that path — each poll 404'd and the page never reloaded after
the scan completed. Report the aggregate status of all four scanners and
omit pageType so every initialization page accepts the update.
2026-08-24 14:17:06 +08:00
Will Miao 3afec0a0be fix(ui): fall back to folder root when persisted active folder no longer exists
restoreSelectedFolder trusted localStorage blindly: a stale activeFolder
(moved/deleted, or saved while the tree was still empty) left the grid
filtered to a nonexistent folder with a phantom breadcrumb and no way to
recover short of clicking the root breadcrumb. Validate the persisted
path against the freshly loaded tree and reset to root when it is gone;
skip validation when the tree load failed so transient errors don't wipe
the saved location.
2026-08-24 14:17:06 +08:00
Will Miao 06c270a6e1 fix(recipes): show initialization screen and auto-reload during first scan
The recipes page always rendered with is_initializing=False, so a cold
start displayed an empty grid that never updated until a manual refresh.
Mirror the model pages: gate render_page on the scanner state, broadcast
init progress from RecipeScanner (including a completion message, and a
failure fallback so the page never stalls), and teach initialization.js
to detect the /loras/recipes page before the generic /loras match.
2026-08-24 14:17:06 +08:00
Will Miao 87e93636dc fix(recipes): wait for in-flight cache initialization instead of returning empty cache
get_cached_data() claimed to wait for a running initialization but
actually returned the placeholder empty cache, so API requests during
startup saw zero recipes. The initializing flag was also set only after
the LoRA scanner wait, leaving an unguarded window. Mark initialization
before the first await and have callers await the in-flight task.
2026-08-24 14:17:06 +08:00
Will Miao 074d1f2e51 feat(ui): improve tag autocomplete toggle discoverability in prompt nodes
- Add Tag Autocomplete ON/OFF entry to the Prompt (LoraManager) node
  right-click menu, cross-referencing the slash commands
- Show the current autocomplete state (/autocomplete or /noautocomplete
  hint) below the slash command list
- Show a one-time dismissible tip in the suggestion dropdown on first use
- Clarify toggle command labels (Turn autocomplete ON/OFF) and cross-link
  all three entry points in the settings tooltip
- Share the setting write path via setLoraManagerSettingValue()
2026-08-24 12:21:31 +08:00
Will Miao 40f922b0e8 fix(ui): right-anchor license icons and delete button as one group in model modal 2026-08-24 11:39:17 +08:00
Will Miao a7214b6cff fix(i18n): translate remaining workflow-related UI strings 2026-08-24 09:31:17 +08:00
Will Miao 8ca66e72eb feat(ui): add delete button and Del shortcut to model and recipe modals 2026-08-24 09:27:00 +08:00
Will Miao 90be5799e4 fix(ui): preserve group editor scroll position when toggling tags 2026-08-24 08:17:47 +08:00
Will Miao 1a93b0eca2 feat(recipes): add prev/next navigation buttons and keyboard shortcuts to recipe modal 2026-08-24 08:15:19 +08:00
Will Miao c2360a35ad fix(ui): show empty folders as move and download destinations (#999) 2026-08-23 21:09:16 +08:00
Will Miao 030a32f8fa feat(ui): add hash search option and de-emphasized hash display in model modal 2026-08-23 10:09:55 +08:00
Will Miao 25e72b43ce fix(download): disable netrc auto-auth to avoid Authorization header conflict (#1070)
With trust_env=True, aiohttp auto-loads credentials from ~/.netrc (e.g. a
'machine civitai.red' or 'default' entry) and refuses to combine them with
the explicit Authorization: Bearer header, aborting every authenticated
CivitAI request with 'Cannot combine AUTHORIZATION header with AUTH
argument or credentials encoded in URL'.
2026-08-22 19:33:01 +08:00
Will Miao 41e9883daa test(recipe): cover send-workflow frontend paths 2026-08-21 21:09:58 +08:00
Will Miao ae461ebc81 fix(registry): replace one %s placeholder per log argument 2026-08-21 21:09:58 +08:00
Will Miao 3ebf256c5d feat(recipe): send embedded recipe workflow to ComfyUI canvas 2026-08-21 21:09:58 +08:00
Will Miao 0905e2be6e fix(recipes): restore primary style on checkpoint Send to ComfyUI button 2026-08-21 10:00:08 +08:00
Will Miao bd380bc1a1 fix(ui): replace stale command abbreviations in autocomplete messages 2026-08-21 09:20:11 +08:00
Will Miao cb4fd3a0e6 refactor(ui): split settings modal into section templates with shared macros 2026-08-21 09:14:52 +08:00
Will Miao bbe0acac5c fix(ui): keep loras widget context menu within viewport bounds 2026-08-21 08:07:50 +08:00