# Module reference Facts for module authors, in tables. For the how and why, read [Writing a module](writing-a-module.md). Everything here is read from `core/contract.py`, `core/schema.py`, `core/registry.py` and the shipped modules. If this page and the code ever disagree, the code wins: please fix the page. ## Announcement constants Read by `core/registry.py` from the module's `__init__.py` (the `FIELDS` table). A constant the registry does not know is ignored. | Constant | Python type | Default | Rule | |---|---|---|---| | `ID` | `str` | none | Required, non-empty. Unique across modules (a second claimant is left out and said). Keep it free of `.`: a fault is traced back to a module from its hook key `funpack.`. | | `TITLE` | `str` | none | Required, non-empty. | | `MOUNT` | `str` | `""` | Required and non-empty when the module has `SETTINGS`. | | `SETTINGS` | `dict` | `{}` | `{key: setting}`. Must be a dict. | | `STAGE` | `str` | `"sampling"` | One of `load`, `conditioning`, `latent`, `guidance`, `sampling`, `post`. | | `CATEGORY` | `str` | `""` | `""` or one of `continuity`, `guidance`, `conditioning`, `sampling`, `post`, `system`. Empty shows under "Other". | | `STATUS` | `str` | `"experimental"` | `proven` or `experimental`. | | `REQUIRES` | `list[str]` | `[]` | Model traits that must all be present. | | `USES` | `list[str]` | `[]` | Capabilities this module asks other modules for. | | `USES_WHEN` | `dict` | `{}` | Same shape as `when`: own settings under which it asks. Keys must be its own settings. | | `ROLES` | `list[str]` | `[]` | Pipeline role `at` names it acts through. Inert in a pipeline where none is filled. | | `AFTER`, `BEFORE` | `list[str]` | `[]` | Module ids. Order within a stage. | | `HOOKS` | `list[str]` | `[]` | Hook points a sampler must offer for a `sampler_modifier`. | | `NODES` | `list[type]` | `[]` | Classes with `define_schema` and `execute` (V3 `io.ComfyNode`). | | `TRAITS` | callable | none | `traits(model) -> iterable[str]`. | | `PROVIDES` | `dict[str, callable]` | `{}` | Capability name to function. Names must be non-empty strings. | `CONTRACT_VERSION` in `core/contract.py` is `1`. It is sent to the browser as `contract` in `/funpack/api/modules`. Core does not read a version from modules, so there is nothing for a module to declare. ## Setting keys | Key | Applies to | Notes | |---|---|---| | `type` | all | `bool`, `int`, `float`, `enum`, `text`, `multiline`, `path`, `color`. | | `default` | all | Required. `bool` is a bool; `int` is an int (not a float); `float`/`int` are finite and inside `min`..`max`; `enum` is one of the options; `color` is `#rgb`, `#rrggbb` or `#rrggbbaa`; the rest are strings. | | `min`, `max` | `int`, `float` | Finite numbers, `min <= max`. A value outside is refused at run time. | | `step` | `int`, `float` | Positive. | | `options` | `enum` | Non-empty list of `{"value": scalar, "label": str}`. No repeated values. | | `label` | all | String. Unique within the module (test). | | `hint` | all | String. Required by test, one plain sentence. | | `unit` | numbers | String. Shown after the hint. | | `placeholder` | text types | String. Used by the panel renderer, not by the Engine page. | | `ui` | all | A renderer allowed for the type (next table). | | `when` | all | `{sibling_key: value or [values]}`. Row shown only while every condition holds. Sibling must exist; an enum value must be one of its options; no self-reference. | The panel renderer also reads `precision`, `presets` (for `macroSlider`) and an option's `icon` and `hint`. The schema does not check them. ### Renderers Allowed `ui` per type (the first is the default for `composer/panel.js`): | Type | Allowed `ui` | |---|---| | `bool` | `checkboxRow`, `toggle` | | `int`, `float` | `number`, `slider`, `stepper`, `macroSlider` | | `enum` | `select`, `segmented`, `radioGroup`, `filterList`, `wheel` | | `text` | `input`, `search` | | `multiline` | `textarea`, `autoTextarea` | | `path` | `filterList` | | `color` | `swatch` | What **Settings > Engine** and the **Sampler** window draw (`features/settings/field.js`), whatever `ui` says: | Type | Drawn as | |---|---| | `bool` | toggle with label and hint | | `int`, `float` | slider if `ui` is absent or `slider` and both `min` and `max` are set, else a number box | | `enum` | select | | `color` | swatch | | `multiline` | text area (3 rows) | | `text`, `path` | text box (commits on blur or Enter) | ## Stages and order `STAGE` order is the list above. Within a stage modules run in id order unless `AFTER`/`BEFORE` say otherwise. Shipped modules today: `load` (loaders, models, pipeline, taste), `conditioning`, `latent`, `guidance` (taste-steered techniques), `sampling`, `post`. ## Categories Used only to group settings in Settings > Engine and to pick what the Sampler window shows (`sampling`). Order on the page: Continuity, Guidance, Conditioning, Sampling, Post, System, Other. ## Pipeline roles A slot in a pipeline lists `roles`: `{"at": ..., "input": ..., "label": ..., "drives": ...}`. `at` is where the input appears in the app. Names in use: | `at` | Shown as | |---|---| | `generation.prompt` | the prompt | | `project.negative` | the project's negative prompt | | `project.video` | width, height, length, frame rate (`drives`: `frames` or `fps`) | | `generation.sampling` | steps, sampler, scheduler (Sampler window) | | `generation.seed` | the seed | | `generation.model` | the model choice | | `assets.source_image` | the start picture (what `continuity` needs) | A role naming a place the app does not offer is simply not shown. ## Traits Read off any loaded model by core (`traits.universal`): | Trait | True when | |---|---| | `temporal_latent`, `spatial_latent`, `waveform_latent` | the latent has 3, 2 or 1 axes | | `temporal_compression` | the latent format compresses time | | `predict_` | the model type, lowercased (`predict_eps`, `predict_flow`, ...) | Announced by model modules: `audio_stream` (H3, LTX), `dit_block_hooks` (H3: honours block replacement and routes attention through the override), `adaln_modalities` (H3 with the AdaLN projection), `ltx_av` (LTX audio+video). Add new ones in the model's own module. Modality is deliberately not a trait. ## Capabilities A capability is a name in `PROVIDES`. Core defines none: the contract is between the provider and the askers. All of these are in use today. `None` as an answer from a model-level provider means "not my model"; a raise means "it was mine and it broke". ### Plugging into a run | Name | Provided by | Signature | Called by | |---|---|---|---| | `modifier` | most sampling and guidance modules | `install(patcher, values, key) -> str or None` | `FunPack Load Modifiers` | | `sampler_modifier` | none yet | `make(values, accepts) -> object with active(ctx), process(ctx, latent)` or `None` | `FunPack Sampler` via `core/chain.py` | | `settings_sink` | `sampling_modifiers` | `() -> {"node": node id, "input": input name}` | the manifest; the app writes settings there | | `cache_key` | `phrase_probe` | `() -> value` that changes with its switch | `FunPack Load Modifiers` cache key | | `guards_off` | `full_control` | `(settings) -> bool` | modifier nodes and the sampler | | `routes` | several | `routes(table, base, web)` | `core/routes.py`, base `/funpack/api/m/` | ### Pipelines | Name | Provided by | Signature | |---|---|---| | `default_pipeline` | `pipeline` | `() -> list of slots` | | `pipeline_presets` | each model module | `() -> [{"id", "title", "slots"}]` | | `optional_nodes` | H3, LTX | `() -> {node class: {"pass": {output index: input name}}}` slots that may be skipped when their module is absent | | `upgrade_slots` | `output_save` | `(slots, outputs) -> slots` migrates old saved pipelines | ### Knowing a model | Name | Signature | Notes | |---|---|---| | `detect` | `(keys) -> bool` | keys are tensor names from a safetensors header. Never load to answer. | | `probe_traits` | `(keys) -> list[str]` | traits knowable from the header alone | | `empty_latent` | `(model, width, height, length, batch_size=1) -> {"samples": ...}` or `None` | claim by identity | | `decode` | `(latent, model=None, vae=None, audio_vae=None, tile_size=0) -> result` or `None` | claim by identity | | `video_stream` | `(x, named) -> (video, rebuild)` or `None` | the picture inside a packed latent; `core/streams.video_of` asks | | `text_rows` | `(model_kwargs, length) -> bool mask` or `None` | which conditioning rows are the prompt's words | | `text_tokenizer` | `(clip) -> tokenizer` or `None` | a tokenizer that returns offsets | | `prompt_rows` | `(meta, cond_len, prompt_tokens) -> (start, end)` or `None` | | | `video_time_rows` | `(samples, t0, t1) -> (rows, start, end)` or `None` | window in seconds | | `target_rows` | `(mod_segments, seq_len, device, stream) -> bool mask` or `None` | `stream` is `"video"` or `"audio"` | | `query_rotation` | `(rope_freqs, seq_len) -> rotate(x, rows, inverse=False)` or `None` | | | `anchor_pin` | `(kwargs, transform) -> kwargs` or `None` | | | `rescale_conditioning` | `(conditioning, height, width) -> (conditioning, count)` or `None` | | | `latent_upscaler` | `(state_dict) -> object with funpack_latent_upscale(x, scale)` or `None` | | | `training_adapt` | `(model) -> {name, step, video, batch_limit, fps, frames, pack, shims, silence}` or `None` | what LoRA training must know about a model whose latent is not a plain tensor (H3 packs picture and sound). `shims()` is a context manager that applies the repairs only for the length of a run | ### Learning | Name | Provided by | Signature | |---|---|---| | `taste_store` | `taste` | `(patcher) -> handle` with `collect`, `rows`, `capture`, `direction`, `directions`, `describe`; `None` without a key | | `taste_kind` | `taste` | `(key, kind) -> Kind` for one kind of one key | | `taste_latest_key` | `taste` | `() -> the key the most recent capture went to, or None` | | `on_rating` | `taste` calls every module that provides it | `(prompt_id, rating, axis) -> None` | ## An add-on module: LoRA training `modules/training/lora/` is announced as `training_lora` and switched on in Settings > Modules (the `lora_training` add-on, which asks for `onnxruntime`, needed only by the automatic tagger). It is a node-and-routes module with no settings of its own: the window passes every value to the node. | File | What it holds | |---|---| | `store.py` | Training sets as JSON under `training/sets/`. An item points at a Bin file or an output file; the path is resolved at run time, inside its root. | | `rated.py` | Finds rated results in the saved projects and maps a rating to `liked`, `disliked`, `bad_composition` or `bad_image`. | | `captions.py` | Tag text helpers, shared in spirit with `app/ui/features/training/captions.js`. | | `taggers.py` | WD tagger models (ONNX) downloaded from Hugging Face on request, or any repo with `model.onnx` and `selected_tags.csv`; one tagging job at a time. | | `media_io.py`, `frames.py` | Pictures and clips as tensors, fitted to a size bucket. | | `plan.py` | Builds the graph a run is queued as: the pipeline's own loaders (ids `model`, `clip`, `vae`, `audio_vae`, or the first of each kind) feed `FunPackTrainLora`. | | `train.py`, `nodes.py` | The run, on ComfyUI's own Train LoRA node. Latents and conditioning are built from the set; each step's loss goes to `jobs.py`; Stop and ComfyUI's interrupt end it at the next step. | | `jobs.py` | The run in progress and finished run records under `training/runs/`. | "Learn what I like" trains on liked results and your own media; "learn what to avoid" trains on the disliked ones, for a LoRA applied at negative strength. This is not VADER (reward gradients), which stays parked. The diffusion model loader sets `cached_patcher_init` for a plain file, because the trainer clones the model from it. An H3 run uses the repairs in `modules/models/minimax_h3/training.py`. **UNTESTED on real weights or hardware**: proven only on a tiny H3 and a stand-in text encoder and VAEs, on CPU. ## Helpers a module may import from `_core` `from ..._core import ...` offers exactly these: `addons`, `chain`, `config`, `contract`, `control`, `dit_hooks`, `graph`, `input_steer`, `log`, `media`, `patching`, `projects`, `rated_dial`, `registry`, `relations`, `run`, `schema`, `shortcuts`, `streams`, `temp_files`, `traits`, `vae_tiles`. Adding one means editing `modules/_core.py` in both of its branches. Most used: | Helper | For | |---|---| | `log` | `info`, `alert`, `warning`, `critical`, `once`, `broke`, `failed` | | `registry` | `registry.current().ask(...)`, `.providers(...)`, `.specs` | | `patching` | `tag`, `strip`, `guard`, `Dropped` | | `dit_hooks` | `add_block_hook`, `add_attention_override`, `current_step`, `late_half`, `last_step`, `probing`, `weak_branch`, `parse_blocks`, `block_count`, `publish` | | `streams` | `video_of`, `av_video_stream`, `model_args`, `with_options` | | `traits` | `has_block`, `traits_of` | | `control` | `Unavailable` | ## Served paths | Path | What | |---|---| | `/funpack/` | the app (`index.html`) | | `/funpack/api/modules` | the manifest (`?traits=a,b` narrows to a model) | | `/funpack/api/m//...` | a module's own routes | | `/funpack/app/...` | the app. Allowed types: `.js .css .html .woff2 .svg`. |