Files
Ilker ManapandClaude Opus 5.5 b6bfc33ef7
CI / test (push) Successful in 9m7s
Any ComfyUI workflow runs as a job, from the inputs its catalog entry declares
A model's catalog entry now says what its workflow takes and makes:
comfyui.inputs, each a placeholder of its name with a type (text, int,
float, bool, choice, image, mask, audio, video, file), bounds, choices
and a default; and comfyui.output, which gains video, audio and files
beside pictures and meshes. POST /v1/workflows/runs with {model, inputs}
checks a request against them before a job is written — a required input
left out, an unknown one, a number out of bounds, a song sent as a
picture, another owner's file id are each refused with what was wrong —
and the job worker replays it to a replica, where the files go into
ComfyUI's input directory, the workflow is filled and run, and the first
file of the model's output kind is the job's result. /v1/models lists
each ComfyUI model's workflow, so a client can build its form.

A PNG result carries the licence in an iTXt chunk, as a GLB does in
asset.extras. A file whose server named no type is typed by its name
before its bytes, so a WAV is audio/wav, not audio/wave.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 14:28:15 +02:00

48 lines
2.2 KiB
Go

package catalog
import (
"strings"
"testing"
)
func TestAWorkflowsInputsAreCheckedWhenTheCatalogIsBuilt(t *testing.T) {
one := 1.0
zero := 0.0
run := func(out string, inputs ...Input) Model {
return comfyModel("/w/ace.safetensors", &ComfyUI{Workflow: "w.json", Output: out, Inputs: inputs})
}
cat, err := New([]Model{run(OutputAudio,
Input{Name: "tags", Type: InputText, Required: true},
Input{Name: "strength", Type: InputFloat, Min: &zero, Max: &one, Default: 0.5},
Input{Name: "style", Type: InputChoice, Choices: []string{"pop"}},
Input{Name: "clip", Type: InputVideo, MaxMB: 512})})
if err != nil {
t.Fatal(err)
}
m, _ := cat.Resolve("flux")
if !m.RunsWorkflows() || m.MakesImages() || m.MakesMeshes() || m.ComfyOutput() != OutputAudio || len(m.ComfyInputs()) != 4 {
t.Errorf("an audio model: runs %v pictures %v meshes %v output %q", m.RunsWorkflows(), m.MakesImages(), m.MakesMeshes(), m.ComfyOutput())
}
if m.ComfyInputs()[3].MaxBytes() != 512<<20 || (Input{Type: InputImage}).MaxBytes() != 16<<20 {
t.Error("a file input's bound")
}
for name, c := range map[string]struct {
m Model
want string
}{
"an unknown kind": {run("hologram"), "not one of pictures"},
"video, no workflow": {comfyModel("/w/x", &ComfyUI{Output: OutputVideo}), "makes video names its workflow"},
"a capital name": {run(OutputAudio, Input{Name: "Tags", Type: InputText}), "lowercase"},
"an unknown type": {run(OutputAudio, Input{Name: "x", Type: "colour"}), "not one of text"},
"a choice of nothing": {run(OutputAudio, Input{Name: "x", Type: InputChoice}), "no choices"},
"min above max": {run(OutputAudio, Input{Name: "x", Type: InputInt, Min: &one, Max: &zero}), "min above max"},
"required and default": {run(OutputAudio, Input{Name: "x", Type: InputInt, Required: true, Default: 3}), "one or the other"},
"a file's default": {run(OutputAudio, Input{Name: "x", Type: InputImage, Default: "a.png"}), "cannot have a default"},
"twice": {run(OutputAudio, Input{Name: "x", Type: InputText}, Input{Name: "x", Type: InputInt}), "declared twice"},
} {
if _, err := New([]Model{c.m}); err == nil || !strings.Contains(err.Error(), c.want) {
t.Errorf("%s: %v", name, err)
}
}
}