ComfyUI/comfy_extras/nodes_math.py
dante01yoon 094e5c25a7 fix: use TemplateNames, cap inputs at 26, improve error message
Address Kosinkadink review feedback:
- Switch from Autogrow.TemplatePrefix to Autogrow.TemplateNames so input
  slots are named a-z, matching expression variables directly
- Cap max inputs at 26 (a-z) instead of 100
- Simplify execute() by removing dual-mapping hack
- Include expression and result value in error message

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 18:00:05 +09:00

75 lines
2.3 KiB
Python

"""Math expression node using JSONata for evaluation.
Provides a ComfyMathExpression node that evaluates JSONata expressions
against dynamically-grown numeric inputs.
"""
from __future__ import annotations
import jsonata as jsonata_lib
from typing_extensions import override
from comfy_api.latest import ComfyExtension, io
def _positional_alias(index: int) -> str:
"""Convert 0-based index to single letter: 0->a, 1->b, ..., 25->z."""
return chr(ord("a") + index)
class MathExpressionNode(io.ComfyNode):
"""Evaluates a JSONata expression against dynamically-grown inputs."""
@classmethod
def define_schema(cls) -> io.Schema:
template = io.MatchType.Template(
"num", allowed_types=[io.Float, io.Int]
)
autogrow = io.Autogrow.TemplateNames(
input=io.MatchType.Input("value", template=template),
names=[_positional_alias(i) for i in range(26)],
min=1,
)
return io.Schema(
node_id="ComfyMathExpression",
display_name="Math Expression",
category="math",
search_aliases=[
"expression", "formula", "calculate", "eval", "math"
],
inputs=[
io.String.Input("expression", default="a + b"),
io.Autogrow.Input("values", template=autogrow),
],
outputs=[
io.MatchType.Output(
template=template, display_name="result"
),
],
)
@classmethod
def execute(
cls, expression: str, values: io.Autogrow.Type
) -> io.NodeOutput:
context: dict = dict(values)
context["values"] = list(values.values())
result = jsonata_lib.Jsonata(expression).evaluate(context)
if isinstance(result, bool) or not isinstance(result, (int, float)):
raise ValueError(
f"Math Expression '{expression}' must evaluate to a numeric result, "
f"got {type(result).__name__}: {result!r}"
)
return io.NodeOutput(result)
class MathExtension(ComfyExtension):
@override
async def get_node_list(self) -> list[type[io.ComfyNode]]:
return [MathExpressionNode]
async def comfy_entrypoint() -> MathExtension:
return MathExtension()