mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-08-28 16:31:28 -03:00
9.6 KiB
9.6 KiB
AGENTS.md
This file provides guidance for agentic coding assistants working in this repository.
Overview
ComfyUI LoRA Manager is a comprehensive LoRA management system for ComfyUI that combines a Python backend with browser-based widgets. It provides model organization, downloading from CivitAI/CivArchive, recipe management, and one-click workflow integration.
Development Commands
Backend Development
# Install dependencies
pip install -r requirements.txt
pip install -r requirements-dev.txt
# Run standalone server (port 8188 by default)
python standalone.py --port 8188
# Run all backend tests
pytest
# Run specific test file
pytest tests/test_recipes.py
# Run specific test function
pytest tests/test_recipes.py::test_function_name
# Run backend tests with coverage
COVERAGE_FILE=coverage/backend/.coverage pytest \
--cov=py --cov=standalone \
--cov-report=term-missing \
--cov-report=html:coverage/backend/html \
--cov-report=xml:coverage/backend/coverage.xml \
--cov-report=json:coverage/backend/coverage.json
Frontend Development (LoRA Manager Web UI)
# Install dependencies (root and Vue widgets)
npm install
cd vue-widgets && npm install && cd ..
npm test # Run all tests (JS + Vue)
npm run test:js # Run JS tests only
npm run test:vue # Run Vue widget tests only
npm run test:watch # Watch mode (JS tests only)
npm run test:coverage # Generate coverage report
Vue Widget Development
cd vue-widgets
npm install
npm run dev # Build in watch mode
npm run build # Build production bundle
npm run typecheck # Run TypeScript type checking
npm test # Run Vue widget tests
npm run test:watch # Watch mode
npm run test:coverage # Generate coverage report
Localization
# Sync translation keys after UI string updates
python scripts/sync_translation_keys.py
Locale files are in locales/ (en, zh-CN, zh-TW, ja, ko, fr, de, es, ru, he).
Code Style
Python
Imports & Formatting
- Use
from __future__ import annotationsfor forward references - Group imports: standard library, third-party, local (blank line separated)
- Use
TYPE_CHECKINGguard for type-checking-only imports - Absolute imports within
py/:from ..services import X - PEP 8 with 4-space indentation, type hints required
Naming Conventions
- Files:
snake_case.py, Classes:PascalCase, Functions/vars:snake_case - Constants:
UPPER_SNAKE_CASE, Private:_protected,__mangled
Error Handling & Async
- Use
logging.getLogger(__name__), define custom exceptions inpy/services/errors.py async deffor I/O,@pytest.mark.asynciofor async tests- Singleton with
asyncio.Lock: seeModelScanner.get_instance() - Return
aiohttp.web.json_responseorweb.Response
JavaScript/TypeScript
Imports & Modules
- ES modules:
import { app } from "../../scripts/app.js"for ComfyUI - Vue:
import { ref, computed } from 'vue', type imports:import type { Foo } - Export named functions:
export function foo() {}
Naming & Formatting
- camelCase for functions/vars/props, PascalCase for classes
- Constants:
UPPER_SNAKE_CASE, Files:snake_case.jsorkebab-case.js - 2-space indentation preferred (follow existing file conventions)
- Vue Single File Components:
<script setup lang="ts">preferred
Widget Development
- Prefer vanilla JS for
web/comfyui/widgets; avoid framework dependencies (except the Vue widgets invue-widgets/) - ComfyUI:
app.registerExtension(),node.addDOMWidget(name, type, element, options) - Event handlers via
addEventListeneror widget callbacks - Shared utilities:
web/comfyui/utils.js - Dual-mode rendering patterns (canvas vs Vue): see
docs/comfyui-dual-mode-widgets.md
Vue Composables Pattern
- Use composition API:
useXxxState(widget), return reactive refs and methods - Guard restoration loops with flag:
let isRestoring = false - Build config from state:
const buildConfig = (): Config => { ... }
Architecture
Dual Mode Operation
The system runs in two modes:
- ComfyUI plugin mode: Integrates with ComfyUI's PromptServer, uses
folder_pathsfor model discovery - Standalone mode:
standalone.pymocks ComfyUI dependencies, reads paths fromsettings.json - Detection:
os.environ.get("LORA_MANAGER_STANDALONE", "0") == "1"
Backend Entry Points
__init__.py— ComfyUI plugin entry: registers nodes viaNODE_CLASS_MAPPINGS, setsWEB_DIRECTORY, callsLoraManager.add_routes()standalone.py— Standalone server: mocksfolder_pathsand node modules, starts aiohttp serverpy/lora_manager.py— MainLoraManagerclass that registers all HTTP routes
Service Layer
ServiceRegistrysingleton for DI, services useget_instance()classmethodBaseModelServiceabstract base →LoraService,CheckpointService,EmbeddingServiceModelScannerbase →LoraScanner,CheckpointScanner,EmbeddingScannerfor file discovery with hash-based deduplicationPersistentModelCache(SQLite) for metadata persistenceMetadataSyncService— background sync from CivitAI/CivArchive APIsSettingsManager— settings with schema migration supportWebSocketManager— real-time progress broadcastingModelServiceFactory— creates the right service for each model type- Use cases in
py/services/use_cases/orchestrate complex business logic (auto-organize, bulk refresh, downloads) - Separate scanners (discovery) from services (business logic)
- Handlers in
py/routes/handlers/are pure functions with deps as params
Model Types & Routes
- API endpoints follow
/loras/*,/checkpoints/*,/embeddings/*patterns - Route registrars organize endpoints by domain:
ModelRouteRegistrar,RecipeRouteRegistrar, etc. - Request handlers in
py/routes/handlers/implement route logic - All routes use aiohttp, return
web.json_responseorweb.Response
Recipe System
- Base:
py/recipes/base.py, Enrichment:RecipeEnrichmentServiceinpy/recipes/enrichment.py - Parsers:
py/recipes/parsers/for PNG metadata, JSON, and workflow formats
Custom Nodes
- Location:
py/nodes/, all nodes registered in__init__.py - Each node class has a
NAMEclass attribute used as key inNODE_CLASS_MAPPINGS - Standard ComfyUI node pattern:
INPUT_TYPES()classmethod,RETURN_TYPES,FUNCTION
Configuration
py/config.pymanages folder paths for models and handles symlink mappings- Auto-saves paths to
settings.jsonin ComfyUI mode
Frontend UI Architecture
1. LoRA Manager Web UI
- Location:
./static/(JS/CSS) and./templates/(HTML) - Tech: Vanilla JS + CSS, served by the hosting server (ComfyUI app in plugin mode,
standalone.pyin standalone mode) - Tests:
tests/frontend/**/*.test.js(vitest + jsdom)
2. ComfyUI Custom Node Widgets
- Location:
./web/comfyui/(Vanilla JS) +./vue-widgets/(Vue) - Primary styles:
./web/comfyui/lm_styles.css(NOT./static/css/) - Vue widgets: Vue 3 + TypeScript + PrimeVue + vue-i18n, e.g.
LoraPoolWidget,LoraRandomizerWidget,LoraCyclerWidget,AutocompleteTextWidget - Vue builds to
./web/comfyui/vue-widgets/; auto-built on ComfyUI startup viapy/vue_widget_builder.py, typecheck viavue-tsc - Widget registration:
app.registerExtension()andgetCustomWidgetshooks;node.addDOMWidget(...)embeds HTML in LiteGraph nodes - See
docs/dom_widget_dev_guide.mdfor the DOMWidget development guide
Testing
Backend (pytest)
- Config in
pytest.ini:--import-mode=importlib, testpaths=tests - Fixtures in
tests/conftest.pymock ComfyUI dependencies; usetmp_path_factoryfor isolation - Markers:
@pytest.mark.asyncio,@pytest.mark.no_settings_dir_isolation(tests needing real settings paths)
Frontend (vitest)
- Vanilla JS tests:
tests/frontend/**/*.test.jswith jsdom; setup intests/frontend/setup.js - Vue widget tests:
vue-widgets/tests/**/*.test.tswith jsdom +@vue/test-utils
Key Integration Points
- Settings: Stored in the user config directory (via
platformdirs) or portable mode ("use_portable_settings": true) - CivitAI/CivArchive: API clients for metadata sync and model downloads; CivitAI API key stored in settings
- Symlinks: Config scans symlinks to map virtual→physical paths; fingerprinting prevents redundant rescans
- WebSocket: Broadcasts real-time progress for downloads, scans, and metadata sync
- Model scanning flow: Walk folders → compute hashes → deduplicate → extract safetensors metadata → cache in SQLite → background CivitAI sync → WebSocket broadcast
Important Notes
- ALWAYS use English for comments (per copilot-instructions.md)
- Run
python scripts/sync_translation_keys.pyafter adding UI strings tolocales/en.json - Symlinks require normalized paths.
Business paths vs real paths: All stored paths and operation routing use the
original paths as they appear under configured model roots — symlinks are NOT
resolved.
os.path.realpathis only for scanner dedup and the symlink cache. Any path passed toos.remove/os.rename/shutil.moveor validated by a containment check MUST use the business path (i.e.os.path.abspath, notrealpath).
Git / Commit Messages
- Follow the style of recent repository commits when writing commit messages
- Prefer the repo's existing
feat(...),fix(...),chore:style where applicable - If the user has provided a GitHub issue link or issue ID for the task, mention that issue in the commit message, for example
(#871) - When unrelated local changes exist, stage and commit only the files relevant to the requested task