release: 3.5.1 "Auspicious Asparagus"
Release codenames, the way Ubuntu does it: an adjective and a vegetable sharing an initial, advancing alphabetically, one per MAJOR version — so all of 3.x is "Auspicious Asparagus" and only a new major earns a new letter. CODENAMES lives beside funpack_version() because the two are read and shipped together, and rides out on the same git-status payload the About panel already fetches. A major with no entry renders as absence, not as an empty pair of quotes. The test pins the naming rule itself (shared initial, ascending) so a future entry that breaks the pattern fails rather than quietly shipping. Version 3.5.1 in pyproject; CHANGELOG entry covering the whole cycle (the Unreleased section only documented the first few commits of it); README gains a Versions preamble explaining the scheme plus notes for 3.5.0 and 3.5.1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
98d48e78e3
commit
06c3f75080
@@ -2,7 +2,73 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [3.5.1] "Auspicious Asparagus" - 2026-08-11
|
||||
|
||||
FunPack picks up release codenames, Ubuntu-style: an adjective and a vegetable sharing an
|
||||
initial, one per major version. All of 3.x is **Auspicious Asparagus**.
|
||||
|
||||
Otherwise this is a compatibility-and-polish release on top of 3.5.0's second model family.
|
||||
LTX-2.5 landed upstream mid-cycle and turned out to need almost nothing — but the two places
|
||||
it *would* have broken silently are fixed, and several long-standing rough edges went with
|
||||
them.
|
||||
|
||||
### Added
|
||||
- **Release codenames.** Shown under the name in Settings ▸ About, carried on the same
|
||||
payload as the version. A major with no name yet simply shows nothing.
|
||||
- **About reports the machine ComfyUI runs on.** Chip, memory, GPU (name, VRAM and compute
|
||||
capability), free disk, OS, ComfyUI, Python, torch and CUDA — plus which fast-attention
|
||||
backend is actually importable, which answers "is SageAttention really installed on this
|
||||
box" without reading the launch arguments. On a rental the host and the browser are
|
||||
different machines and the host is the interesting one, so the panel says which it means.
|
||||
Every probe degrades to a dash on its own: About is the screen that should still render
|
||||
when the install is broken.
|
||||
- **A schedule for every sampler.** Steps and scheduler belong to the pass, not to the
|
||||
KSampler branch, so any sampler can now be given a computed schedule instead of only a
|
||||
hand-typed sigma list. The frames field became usable in the same pass.
|
||||
- **ALG on any sampler.** The i2v anchor blur used to be locked to FunPack's Distilled Flow
|
||||
sampler. It now runs on whatever sampler is wired — a stock KSampler, Hybrid Euler 2S, a
|
||||
two-evals-per-step sampler like `heun` — by lifting the guidance out of the sampler loop
|
||||
and onto a denoiser proxy driven by the step's sigma.
|
||||
- **H3 audio clock.** MiniMax H3 denoises video and audio on two different flow schedules
|
||||
but hands the sampler one sigma grid, so the DiT reconciles them with a start-of-step
|
||||
slope that badly overshoots on a few-step schedule — heard as distortion. `h3_audio_clock`
|
||||
swaps that for the chord actually spanning the step. One scalar multiply, no extra model
|
||||
call. It works with stock ComfyUI samplers too, and stands itself down with a console note
|
||||
on samplers that evaluate twice per step rather than guessing.
|
||||
- **Projects remember your Editor settings**, so a fresh rental does not reset your
|
||||
preferences and shortcut revolver.
|
||||
- **Every Engine setting says what it does, what it costs and what it needs**, in one line.
|
||||
|
||||
### Fixed
|
||||
- **LTX-2.5 compatibility.** 2.5 reuses the same model classes behind new config flags, so
|
||||
nearly everything binds unchanged — but two places assumed 2.3 specifics and would have
|
||||
failed *silently*. The video/audio conditioning split was a hardcoded width table; from
|
||||
2.5 on ComfyUI reads those widths off the checkpoint, and an unrecognised pair made the
|
||||
split return the full width, steering the audio text context along with the video. The
|
||||
only symptom would have been degraded audio. The split is now measured off the live model,
|
||||
with the table kept as a fallback. Separately, decode-time noise reached only the conv
|
||||
decoder: 2.5's diffusion decoder takes no timestep and seeds itself, but is an `nn.Module`,
|
||||
so the settings were accepted and then ignored — a knob that read as live and did nothing.
|
||||
It is now capability-tested and says so when it cannot apply.
|
||||
- **`cut_opening_frames` no longer leaves a noisy first frame.** The cut was made in latent
|
||||
space, and the LTX video VAE is causal: latent frame 0 is the temporal origin, while every
|
||||
later frame was generated as a continuation. Slicing the front off the latent promoted a
|
||||
continuation frame to position 0, which then decoded with origin handling it was never
|
||||
generated for. The cut now happens on decoded pixels, after a decode that saw every frame
|
||||
in the context it was sampled in — the way MiniMax H3 already did it. The count was wrong
|
||||
too: a latent cut could only remove whole latent frames and shortened the clip by a
|
||||
different amount than the span it removed. It is exact now, N means N. Video comes from the
|
||||
IMAGES output on a cut run (the Editor already wires it that way); the latent's audio
|
||||
stream is cropped by the same amount of time so sound and picture still start together.
|
||||
- **Guide keyframes are dropped when `second_pass_op` changes the resolution.** They are
|
||||
recorded as token indices into pass 1's grid, so `upscale_2x` left them addressing the
|
||||
wrong tokens — which recent ComfyUI rejects outright and older builds mis-placed silently.
|
||||
Pass 1 still uses every guide; only pass 2 loses them, and the scene report says so.
|
||||
- **One ALG control instead of two.** The Distilled Flow panel had its own switch while the
|
||||
chain sampler's anchor blur did the same thing on any sampler and already drove it — two
|
||||
controls for one behaviour, with precedence depending on which you had touched. The Editor
|
||||
now shows one. Projects carrying the old switch are migrated with their strength and
|
||||
threshold, and told so.
|
||||
- **H3 image conditioning actually reaches the model.** Two independent breaks, both silent,
|
||||
both ending with a generation that ignored the input image entirely and left the prompt to
|
||||
carry the whole shot. The Movie Editor always splits a run into per-scene conditionings,
|
||||
@@ -16,12 +82,26 @@
|
||||
because the sampler's positive comes from Studio. New optional **Chain Sampler ·
|
||||
h3_keyframes** input (auto-wired for H3 projects) salvages the pins — first frame onto the
|
||||
opening scene, last frame re-indexed onto the closing scene's own final frame.
|
||||
- **Spare H3 token tags are trimmed, not thrown away.** An image prompt comes back one tag
|
||||
long, and treating the surplus as corruption discarded the whole vector — which on exactly
|
||||
those prompts left the DiT modulating the picture as if it were text.
|
||||
- **Projects remember which model family they are for.** Every project on disk had no family
|
||||
recorded while the global default said MiniMax H3, so the builder read them as LTXAV and
|
||||
routed the audio VAE at a node H3's graph never builds. That is the reported "phantom Audio
|
||||
VAE port": one root cause, four separate bugs, all fixed.
|
||||
- **A wired `positive_conditioning` satisfies Studio's CLIP requirement**, and an
|
||||
fl2va/ref2va node satisfies H3's AV latent requirement — neither should have been blocking
|
||||
generation.
|
||||
- **Warn only when Studio has no conditioning source at all**, rather than whenever one
|
||||
particular input is empty.
|
||||
|
||||
### Changed
|
||||
- **Guide strengths span the full 0..1 range.** Mid-scene guide, identity pin, prior-scene
|
||||
guide, the per-guide strength field, and the JoyAI memory floor were clamped to 0.25–0.5.
|
||||
That band is the measured audio-safe sweet spot, not a physical limit; it stays in the
|
||||
tooltips and out of the code.
|
||||
- **Project texts reach nodes that encode on their own**, expanded — so a custom encoder in
|
||||
the graph sees the same prompt the pipeline does.
|
||||
- The H3 `v2a_grad_scale` warning chip is gone. The knob does nothing without JoyAI audio
|
||||
memory on any model, so flagging it as an H3 limitation blamed the wrong thing.
|
||||
|
||||
|
||||
@@ -44,7 +44,13 @@ Use only `main` if you want the most stable version of FunPack. Bug reports base
|
||||
|
||||
From time to time other short-lived branches may appear for the most advanced yet most breaking changes and features (e.g. a large rework in progress). I do not recommend using any non-`main` branch unless it's strictly necessary for your workflow — any sort of stability is not guaranteed, and such branches can be force-pushed or deleted at any time.
|
||||
|
||||
## Version 3.0 — Cutting Room and development direction
|
||||
## Versions
|
||||
|
||||
Since **3.5.1**, each major version carries a release codename — an adjective and a vegetable
|
||||
sharing an initial, advancing alphabetically, in the tradition Ubuntu made familiar. All of
|
||||
3.x is **"Auspicious Asparagus"**. It is shown in Settings ▸ About.
|
||||
|
||||
### Version 3.0 — Cutting Room and development direction
|
||||
|
||||
FunPack **3.0** introduces the **Cutting Room** — a dedicated montage editor at `/funpack/movie` inside ComfyUI. It is the primary surface for building multi-scene projects, previewing on a real timeline, and driving FunPack Studio + the LTXAV Scene Chain Sampler without wiring a graph by hand.
|
||||
|
||||
@@ -82,6 +88,22 @@ the model's comfortable window, and a progress readout that says which scene and
|
||||
running. Context windows, which never actually worked before, are fixed — see the
|
||||
[CHANGELOG](CHANGELOG.md) for the full list.
|
||||
|
||||
**3.5.0** adds a **second model family**: MiniMax H3 alongside LTX-2 / LTXAV. A project picks a
|
||||
family, and everything downstream follows from that choice — which nodes the graph emits, which
|
||||
model files the setup asks for, which sampler settings are even applicable, and what counts as a
|
||||
valid scene length. Nothing about LTX-2 changes.
|
||||
|
||||
**3.5.1 "Auspicious Asparagus"** is the first release with a codename, and a
|
||||
compatibility-and-polish pass on top of 3.5.0. **LTX-2.5** works — it reuses the same model
|
||||
classes behind new config flags, so almost everything binds unchanged, and the two places that
|
||||
would have broken *silently* are fixed. **Settings ▸ About** now reports the machine ComfyUI
|
||||
runs on (chip, memory, GPU and VRAM, disk, OS, Python, torch, and which fast-attention backend
|
||||
is actually installed), which is the thing you want at a glance on a fresh rental. **ALG** runs
|
||||
on any sampler rather than only FunPack's Distilled Flow, and its two duplicate switches became
|
||||
one. **`cut_opening_frames`** no longer leaves a noisy first frame — it cuts decoded pixels
|
||||
instead of latents, and the count is now exact. See the [CHANGELOG](CHANGELOG.md) for the full
|
||||
list.
|
||||
|
||||
See [`docs/MovieEditor.md`](docs/MovieEditor.md) for complete Cutting Room documentation.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -39,6 +39,25 @@ def funpack_version() -> str:
|
||||
return m.group(1) if m else ""
|
||||
|
||||
|
||||
# Release codenames, the way Ubuntu does it: an adjective and a vegetable sharing an
|
||||
# initial, advancing alphabetically. Keyed by MAJOR version, so every 3.x release ships
|
||||
# under the same name and only a new major earns a new letter.
|
||||
CODENAMES = {
|
||||
"3": "Auspicious Asparagus",
|
||||
}
|
||||
|
||||
|
||||
def funpack_codename(version: str = "") -> str:
|
||||
"""Codename for `version` (default: the installed one), or "" when its major has none.
|
||||
|
||||
Kept beside funpack_version because they are read together and shipped together; a
|
||||
major with no entry simply has no codename yet, which the UI renders as absence rather
|
||||
than as a blank line.
|
||||
"""
|
||||
major = (version or funpack_version()).split(".", 1)[0].strip()
|
||||
return CODENAMES.get(major, "")
|
||||
|
||||
|
||||
def _current_branch() -> str:
|
||||
proc = _run_git("rev-parse", "--abbrev-ref", "HEAD")
|
||||
if proc.returncode != 0:
|
||||
@@ -115,6 +134,7 @@ def status() -> dict:
|
||||
return {
|
||||
"ok": True,
|
||||
"version": funpack_version(),
|
||||
"codename": funpack_codename(),
|
||||
"branch": branch,
|
||||
"commit": commit,
|
||||
"dirty": dirty,
|
||||
@@ -125,7 +145,8 @@ def status() -> dict:
|
||||
"repo": str(REPO_ROOT),
|
||||
}
|
||||
except GitUpdateError as e:
|
||||
return {"ok": False, "version": funpack_version(), "detail": str(e)}
|
||||
return {"ok": False, "version": funpack_version(),
|
||||
"codename": funpack_codename(), "detail": str(e)}
|
||||
|
||||
|
||||
def pull(branch: str | None = None) -> dict:
|
||||
|
||||
@@ -246,6 +246,15 @@
|
||||
const git = G ? G.get() : null;
|
||||
wrap.append(el("div", "sw-about-mark", "◉"));
|
||||
wrap.append(el("div", "sw-about-name", "FunPack"));
|
||||
// Ubuntu-style release codename, carried on the git status alongside the version.
|
||||
// Keyed by major, so it reads "3 “Auspicious Asparagus”". Omitted entirely when the
|
||||
// backend is older than the field or the major has no name yet — an empty quoted
|
||||
// string would look like a bug.
|
||||
if (git?.codename) {
|
||||
const major = String(git.version || "").split(".")[0];
|
||||
wrap.append(el("div", "sw-about-codename",
|
||||
(major ? major + " " : "") + "“" + git.codename + "”"));
|
||||
}
|
||||
// window.FunPackAppName lets a different frontend sharing this file (e.g. Easy
|
||||
// Gen) relabel the app name without forking the whole section; Editor leaves it
|
||||
// unset and keeps "Cutting Room".
|
||||
|
||||
@@ -1723,6 +1723,8 @@ body.tour-mode { overflow: hidden; }
|
||||
.sw-about-mark { font-size: 56px; line-height: 1; color: var(--amber);
|
||||
text-shadow: 0 0 24px rgba(243,169,60,.45); margin-bottom: 10px; }
|
||||
.sw-about-name { font-family: var(--font-display); font-weight: 700; font-size: 28px; letter-spacing: -.02em; }
|
||||
.sw-about-codename { font-family: var(--font-display); font-size: 14px; font-style: italic;
|
||||
color: var(--amber); letter-spacing: .01em; margin-top: 2px; }
|
||||
.sw-about-sub { font-size: 13px; color: var(--muted); margin-bottom: 18px; }
|
||||
/* Two-column grid rather than centred rows: values now run from short ("3.14") to long
|
||||
("NVIDIA RTX PRO 6000 Blackwell · 95.5 GB · sm_120"), and only a shared column keeps
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
"""Version and release codename, as shown in Settings ▸ About.
|
||||
|
||||
The codename is cosmetic, so the only things worth pinning are the ones that would make it
|
||||
look broken: a major with no name must render as ABSENCE (the panel omits the line), never
|
||||
as an empty pair of quotes, and the pyproject version must stay parseable — About reads it
|
||||
through the same helper the update check does.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from movie_editor.backend import git_update
|
||||
|
||||
|
||||
def test_installed_version_is_parseable():
|
||||
version = git_update.funpack_version()
|
||||
assert version, "About and the update check both read this"
|
||||
parts = version.split(".")
|
||||
assert len(parts) >= 2 and all(p.isdigit() for p in parts[:2]), version
|
||||
|
||||
|
||||
def test_current_major_has_a_codename():
|
||||
assert git_update.funpack_codename() == "Auspicious Asparagus"
|
||||
|
||||
|
||||
def test_codename_is_keyed_by_major_not_by_release():
|
||||
"""Every 3.x release ships under one name; only a new major earns a new letter."""
|
||||
for version in ("3.0.0", "3.5.1", "3.99.12"):
|
||||
assert git_update.funpack_codename(version) == "Auspicious Asparagus"
|
||||
|
||||
|
||||
def test_unknown_major_has_no_codename():
|
||||
"""Absence, not an empty string in quotes — the panel omits the line entirely."""
|
||||
assert git_update.funpack_codename("9.0.0") == ""
|
||||
assert git_update.funpack_codename("") != None # noqa: E711 - never None, always str
|
||||
|
||||
|
||||
@pytest.mark.parametrize("version", ["3", "3.5", "3.5.1", " 3.5.1 "])
|
||||
def test_codename_tolerates_short_and_padded_versions(version):
|
||||
assert git_update.funpack_codename(version) == "Auspicious Asparagus"
|
||||
|
||||
|
||||
def test_codenames_advance_alphabetically():
|
||||
"""The Ubuntu rule this follows: adjective and vegetable share an initial, and the
|
||||
initial advances with the major. A future entry that breaks it should fail here."""
|
||||
for major, name in sorted(git_update.CODENAMES.items()):
|
||||
adjective, _, noun = name.partition(" ")
|
||||
assert noun, f"{name!r} should be two words"
|
||||
assert adjective[0].lower() == noun[0].lower(), f"{name!r} initials differ"
|
||||
majors = sorted(git_update.CODENAMES, key=int)
|
||||
initials = [git_update.CODENAMES[m][0].lower() for m in majors]
|
||||
assert initials == sorted(initials), f"codename initials must ascend: {initials}"
|
||||
|
||||
|
||||
def test_status_carries_the_codename():
|
||||
"""The About panel reads it off the git status payload, not a separate call."""
|
||||
assert "codename" in git_update.status()
|
||||
+3
-1
@@ -2,7 +2,9 @@
|
||||
[project]
|
||||
name = "ComfyUI-FunPack" # Unique identifier for your node. Immutable after creation.
|
||||
description = "An AI movie studio inside ComfyUI: the Cutting Room browser-based video editor with timeline, per-scene prompts and one-click montage generation, plus a self-improving engine that learns from your ratings. Includes the full node stack (Studio, Refiner V2, LTXAV Scene Chain Sampler) for classic graph workflows."
|
||||
version = "3.5.0" # Custom Node version. Must be semantically versioned.
|
||||
version = "3.5.1" # Custom Node version. Must be semantically versioned.
|
||||
# Release codename ("Auspicious Asparagus" for all of 3.x) lives in
|
||||
# movie_editor/backend/git_update.py:CODENAMES — keyed by major, shown in Settings ▸ About.
|
||||
license = { text = "GNU GPL v3" }
|
||||
dependencies = [] # Filled in from requirements.txt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user