14 KiB
Architecture
ComfyUI-Doctor is a ComfyUI custom node extension with three main surfaces:
- a Python package that loads inside ComfyUI and registers PromptServer routes
- browser-side JavaScript that renders the Doctor sidebar and panels
- development/test tooling that keeps host compatibility, API contracts, and UI behavior stable
For route-level details, use the machine-readable OpenAPI contract: openapi.json.
Host Startup
ComfyUI loads the package entry point from __init__.py. That file keeps host-facing responsibilities together:
- install or hand off the startup logger
- initialize the full
SmartLogger - configure LLM rate and concurrency limits
- create the API logger
- collect a system snapshot
- register PromptServer routes through
api_routes.py - expose node mappings and
WEB_DIRECTORY
prestartup_script.py runs earlier in the ComfyUI startup lifecycle. It captures early terminal output before the full package import is available. The main package then hands off that logger so startup logs and runtime logs share one path. Doctor-owned terminal output uses one severity-aware identity, strips ANSI styling from persisted/plain sinks, and selects the full Unicode startup banner only when the effective stream can encode it; otherwise it emits the ASCII fallback.
Backend Layout
The backend is organized around a small entry point and focused service modules:
| Area | Primary files | Responsibility |
|---|---|---|
| Package entry | __init__.py, nodes.py, prestartup_script.py |
ComfyUI loading, node export, startup logging |
| API routes | api_routes.py, services/routes.py |
PromptServer HTTP route registration and route handlers |
| Analysis | analyzer.py, pattern_loader.py, pipeline/, services/prompt_helpers.py |
Error categorization, pattern matching, prompt construction |
| LLM | llm_client.py, session_manager.py, services/llm_provider_adapters.py, services/outbound_policy.py, services/llm/ |
Provider requests, approved destinations and connect-time address checks, retry, proxy policy, rate/concurrency limits |
| Security | security.py, outbound.py, sanitizer.py, services/security/ |
SSRF checks, outbound sanitization, admin guard, error envelopes |
| Logging and health | terminal_output.py, logger.py, services/dynamic_vram_advisory.py |
Canonical terminal output, bounded runtime capture, and isolated nonfatal host advisories |
| Storage | services/doctor_paths.py, history_store.py, services/secret_store.py |
Canonical data paths, history, encrypted credential storage |
| Diagnostics | services/diagnostics/, services/intent/ |
Workflow, dependency, current host model/media/dataset asset folders and loaders, privacy, performance, and signature-pack checks |
| Community | services/community_feedback.py, pipeline/plugins/ |
Feedback preview/submission and scan-only plugin trust reporting |
| Telemetry | telemetry.py, web/statistics-island.js, web/tabs/stats_tab.js |
Local-only opt-in event buffer; controls in the statistics view |
The domain packages under services/llm/, services/security/, services/infra/, and services/community/ provide stable import entry points for code that should not depend on individual implementation file paths.
API Surface
api_routes.py registers the main Doctor and debugger endpoints. Smaller service-owned handlers live in services/routes.py and are attached by api_routes.py.
The public API groups are:
- debugger status, language, and history
- LLM analysis, chat, provider defaults, key verification, and model listing
- admin-gated server-side credential metadata and storage
- statistics, resolution state, and feedback
- runtime health, diagnostics, and plugin trust reports
- local telemetry controls
- resumable jobs and provider capability status
Write-sensitive routes use the Doctor admin guard. The API error shape is centralized through services/api_response.py, and the public contract is captured in docs/openapi.json.
Doctor-owned docs use canonical /doctor/... route paths. Current ComfyUI hosts may also expose /api/doctor/... aliases through host route duplication, but that global alias behavior belongs to the host.
Data Flows
Startup and Logging
prestartup_script.pycaptures early output.__init__.pyimports the package with ComfyUI-relative imports.- Canonical terminal formatting and the encoding-safe startup banner are handed off to the full package.
SmartLogger.install()clears stale advisory state, replays one bounded public host-log snapshot, and then starts the normal runtime processor.- The API logger writes ANSI-free operational events to the Doctor data directory.
- Route registration runs after ComfyUI
serverandaiohttpimports are available.
Error Analysis
- ComfyUI output is captured by
SmartLogger. - The analyzer and pattern pipeline classify the error.
- Node/workflow context is collected and pruned.
- Prompt helpers build a provider-neutral request payload.
- LLM calls go through approved-destination matching, adapter, retry, rate-limit, proxy, connect-time address checks, and outbound-sanitization layers.
- Results are stored in history and exposed to the frontend.
Nonfatal Host Advisory
- Startup replay reads at most 300 entries from the already-loaded public
app.logger.get_logs()buffer without importing host code. - Only the three exact supported DynamicVRAM source messages update the
single bounded advisory slot: the prior and current PyTorch fallback forms
plus the
comfy-aimdofallback. Ordinary warnings and near matches continue through their existing behavior. - The advisory remains outside
ErrorAnalyzer, traceback/history, and latest runtime-error state and is exposed only as an allowlisted projection in/doctor/health. - The legacy and Preact Statistics paths use the same pure text formatter to show fixed Trust & Health guidance without rendering the raw host message.
Chat
- The frontend sends error context, user messages, provider settings, privacy mode, and optional credentials.
- Backend routes resolve credentials from request data, environment variables, local provider behavior, or the server-side store.
- The provider adapter builds the request.
- The route returns JSON or Server-Sent Events for streaming chat.
Diagnostics
- The frontend or caller posts workflow/context data to diagnostics endpoints.
HealthCheckRequestis parsed by the diagnostics runner.- Registered checks inspect workflow lint, dependencies, model assets, privacy/security, runtime performance, and signature packs.
- Reports are saved by the diagnostics store and exposed through report/history endpoints.
services/diagnostics/node_identity.py defines the bounded numeric/opaque-string identity
contract shared by issue targets, workflow lint, signature targets and asset provenance.
web/comfyui_frontend_compat.js resolves raw IDs and real subgraph execution paths with bounded
enumeration; mutations require a unique exact target. Colons inside raw IDs are preserved.
Malformed or duplicate workflow identities are skipped locally, so valid-node findings survive,
but completeness on the malformed nodes is not guaranteed. Workflow lint exempts only the proven
optional Switch and canonical Branch input families; host-selected branch execution checks remain
owned by ComfyUI.
Model asset checks use ComfyUI's live folder_names_and_paths registry as the
authoritative source when available, including custom roots and registered
extensions. Older hosts retain a bounded fixed fallback. Workflow traversal
also inspects bounded definitions.subgraphs content and preserves visible
host plus concrete source provenance for nested assets. Positional widget
values remain authoritative and uniquely matched named values are fallback
only. First-party promoted image/video/output/audio loaders retain concrete
source provenance; arbitrary custom upload-loader behavior is not inferred
without authoritative node definitions. Input/dataset folders and every file
candidate are real-path-contained within registered roots before any
filesystem probe, read, or enumeration.
The exact first-party SAM3DBody_Loader maps its model_file to the
registered detection category before shared-extension inference. An absent
or empty detection category never authorizes a checkpoints fallback.
Telemetry
Telemetry is local-only and disabled by default. When enabled, accepted events are validated against an allowlist and written to the local telemetry buffer. The browser UI can view, export, clear, and toggle telemetry through Doctor endpoints. The integration and stress test lanes use the Playwright harness backend for deterministic telemetry endpoint behavior.
Frontend Layout
ComfyUI loads frontend assets from web/.
| Area | Primary files | Responsibility |
|---|---|---|
| Extension registration | web/doctor.js, web/comfyui_frontend_compat.js |
ComfyUI extension setup and host API compatibility |
| State and API | web/doctor_state.js, web/doctor_api.js, web/doctor_actions.js |
UI state, backend requests, user actions |
| Shell UI | web/doctor_ui.js, web/doctor_right_panel.js, web/doctor_rendering.js, web/doctor_selectors.js |
Sidebar shell, right panel, rendering helpers, selectors |
| Chat | web/chat-island.js, web/tabs/chat_tab.js, web/llm_provider_quick_switch.js |
Chat UI, Preact island, vanilla fallback, provider switch |
| Statistics and diagnostics | web/statistics-island.js, web/tabs/stats_tab.js, web/utils/health_summary.js |
Stats dashboard, shared health/trust formatting, feedback, telemetry |
| Settings | web/tabs/settings_tab.js, web/llm_key_store.js, web/privacy_utils.js |
Provider settings, credential handling, privacy mode |
| Resilience | web/preact-loader.js, web/island_registry.js, web/ErrorBoundary.js, web/global_error_handler.js |
Local Preact loading, island fallback, error boundaries |
Preact islands improve the Chat and Statistics surfaces, but each island has a vanilla fallback so the sidebar remains usable if the island loader fails. Tab activation receives the exact owned pane. Chat island activation and unmount operate on that concrete container, while late async render cleanup is discarded safely after tab-manager destruction rather than acting on detached DOM or an unrelated matching element. Validation-error display is normalized in the frontend so known prompt-validation failures use stable local catalog text and grouping metadata. Public raw errors and graph links can surface eligible input-level errors to a visible subgraph boundary while retaining source provenance; unknown or unproven topology falls back safely without private store access or breaking runtime error reporting.
Security and Storage Boundaries
Security-sensitive behavior is centralized:
- LLM requests only reach operator-approved destinations; outbound URLs are built from the approved entry and resolved addresses are checked at connect time.
- Outbound sanitization prevents raw private context from leaving the process;
AuthorizationandX-API-Keyvalues are always redacted in text and structured payloads, regardless of privacy mode. - Privacy modes control how much local context can be sent to remote providers.
- Write-sensitive endpoints use the admin guard.
- Server-side credentials use the secret store and are never exposed through status APIs.
- Community plugins are scanned for trust metadata without importing plugin code.
- Telemetry is local-only and opt-in.
- Doctor-owned frontend settings opt out of the host frontend's independent setting-change telemetry.
- Plain backend and exception status strings are inserted as literal text; intentional rich chat rendering stays on its separate sanitized path.
- DynamicVRAM health output exposes fixed allowlisted guidance rather than raw host log text, device names, command lines, paths, prompts, or provider data.
Storage should go through services/doctor_paths.py so Desktop, portable, and
standard ComfyUI installations resolve Doctor data consistently. Runtime
identity and storage evidence are separate: a managed Desktop interpreter can
remain classified as Desktop while the data directory comes from ComfyUI's
private system-user API.
Testing Architecture
The full local acceptance gate is scripts/run_full_tests_windows.ps1 on Windows or scripts/run_full_tests_linux.sh on Linux/WSL. It covers:
- supply-chain dependency scan
- detect-secrets
- pre-commit hooks
- host-like package/startup validation
- backend unit tests
- default Playwright E2E
Backend test collection leaves the canonical production __init__.py
byte-stable. Host-shaped package loading is validated separately rather than
renaming the entry point or substituting a backup during pytest collection.
The route harness uses a normal importlib package spec and loader for the unchanged canonical
entry point. Mandatory backend tests run isolated Ruff S102, S307 and E702 checks over all
tracked Python files and verify the scanner with inert failure fixtures. No host reference code
is executed by compatibility checks.
Additional focused lanes:
npm run test:integrationruns@integrationtelemetry tests against the harness backend by default, or a live backend whenCOMFYUI_URLis set.npm run test:stressruns opt-in@stresstelemetry burst/state tests against the harness backend.python scripts/focused_gate.pyruns the supplemental focused security/contract/E2E regression lane. It is not a replacement for the full acceptance gate.python scripts/check_host_compatibility.pyevaluates source-linked contracts with separate frontend runtime lanes for the Desktop bundle, ComfyUI package pin, and standalone frontend source.
Compatibility Principles
- Keep package imports compatible with ComfyUI custom-node loading.
- Keep frontend registration compatible with current and deprecated host sidebar APIs where support exists.
- Keep host compatibility checks version-aware and aligned with prompt queue source metadata, execution event payloads, model registries, nested workflow serialization, validation-error state, setting telemetry, Desktop layout, DynamicVRAM applicability/feature threshold, the SAM3D Body detection-root contract, core-pinned and standalone asynchronous setting handlers, system statistics metadata, job-cancel contracts, and frontend queue/cancel adoption.
- Keep public route changes reflected in
docs/openapi.json. - Keep local harness tests deterministic; live backend tests must be explicit opt-in.