Replace the binary "Enable Reasoning" toggle with a tri-state
Thinking / Reasoning control (auto / on / off) that drives LM Studio's
template-level reasoning switch via chat_template_kwargs.enable_thinking
(nested — a top-level field never reaches the Jinja template):
- off -> suppress: enable_thinking:false + a closed <think></think>
assistant prefill, so a reasoning model answers directly instead
of burning its whole budget on a hidden think trace.
- on -> force: enable_thinking:true.
- auto -> force for families that stay silent by default (Gemma), otherwise
send nothing (Qwen3-style templates already think by default).
Applied to both the responses and chat.completions paths in the text and
image nodes, so reasoning is controlled consistently regardless of which
endpoint the server supports.
Fix the duplicate "Model" field: the web extension used to inject a second
combo widget and hide the backend string widget (unreliably), leaving two
model fields on screen. The model input is now a native io.Combo populated
in place by Refresh Models; `model` is listed in validate_inputs so ComfyUI
skips its "value not in list" check (the real options are only known at
runtime).
Rework the Connect node UI: essentials first (Server URL -> Model ->
Thinking), secondary settings moved to advanced, buttons show progress
labels and toast feedback instead of a blocking alert, and the status line
reports the resolved thinking mode.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
146 lines
3.2 KiB
Python
146 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import sys
|
|
import types
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
if str(REPO_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
TESTS_ROOT = Path(__file__).resolve().parent
|
|
if str(TESTS_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(TESTS_ROOT))
|
|
|
|
|
|
class _Field:
|
|
def __init__(self, **kwargs):
|
|
self.__dict__.update(kwargs)
|
|
|
|
|
|
class _FieldType:
|
|
class Input(_Field):
|
|
pass
|
|
|
|
class Output(_Field):
|
|
pass
|
|
|
|
|
|
class _Schema:
|
|
def __init__(self, **kwargs):
|
|
self.__dict__.update(kwargs)
|
|
|
|
|
|
class _NodeOutput:
|
|
def __init__(self, *values, ui=None):
|
|
self.values = values
|
|
self.ui = ui
|
|
|
|
def __getitem__(self, index: int):
|
|
return self.values[index]
|
|
|
|
def __iter__(self):
|
|
return iter(self.values)
|
|
|
|
def __len__(self) -> int:
|
|
return len(self.values)
|
|
|
|
|
|
class _ControlAfterGenerate:
|
|
randomize = "randomize"
|
|
|
|
|
|
class _ComfyNode:
|
|
pass
|
|
|
|
|
|
class _ComfyExtension:
|
|
async def on_load(self) -> None:
|
|
return None
|
|
|
|
async def get_node_list(self) -> list[type]:
|
|
return []
|
|
|
|
|
|
def _custom_type(_name: str):
|
|
class _Custom:
|
|
class Input(_Field):
|
|
pass
|
|
|
|
class Output(_Field):
|
|
pass
|
|
|
|
return _Custom
|
|
|
|
|
|
class _RoutesRegistry:
|
|
def __init__(self):
|
|
self.handlers: list[tuple[str, object]] = []
|
|
|
|
def get(self, path: str):
|
|
def decorator(handler):
|
|
self.handlers.append((path, handler))
|
|
return handler
|
|
|
|
return decorator
|
|
|
|
|
|
class _PromptServer:
|
|
instance = types.SimpleNamespace(routes=_RoutesRegistry())
|
|
|
|
|
|
def _install_comfy_stubs() -> None:
|
|
io_module = types.SimpleNamespace(
|
|
Schema=_Schema,
|
|
ComfyNode=_ComfyNode,
|
|
NodeOutput=_NodeOutput,
|
|
ControlAfterGenerate=_ControlAfterGenerate,
|
|
String=_FieldType,
|
|
Boolean=_FieldType,
|
|
Int=_FieldType,
|
|
Float=_FieldType,
|
|
Image=_FieldType,
|
|
Combo=_FieldType,
|
|
Custom=_custom_type,
|
|
)
|
|
ui_module = types.SimpleNamespace(
|
|
PreviewText=lambda text: types.SimpleNamespace(text=text)
|
|
)
|
|
|
|
latest_module = types.ModuleType("comfy_api.latest")
|
|
latest_module.io = io_module
|
|
latest_module.ui = ui_module
|
|
latest_module.ComfyExtension = _ComfyExtension
|
|
|
|
comfy_module = types.ModuleType("comfy_api")
|
|
comfy_module.latest = latest_module
|
|
|
|
sys.modules["comfy_api"] = comfy_module
|
|
sys.modules["comfy_api.latest"] = latest_module
|
|
|
|
|
|
def _install_server_stub() -> None:
|
|
server_module = types.ModuleType("server")
|
|
server_module.PromptServer = _PromptServer
|
|
sys.modules["server"] = server_module
|
|
|
|
|
|
_install_comfy_stubs()
|
|
_install_server_stub()
|
|
|
|
# Pytest may import repository __init__.py as a top-level module named "__init__",
|
|
# which breaks relative imports in that file. Pre-seed a shim module for test collection.
|
|
if "__init__" not in sys.modules:
|
|
init_shim = types.ModuleType("__init__")
|
|
init_shim.__file__ = str(REPO_ROOT / "__init__.py")
|
|
sys.modules["__init__"] = init_shim
|
|
|
|
|
|
@pytest.fixture
|
|
def prompt_server_routes():
|
|
routes = _RoutesRegistry()
|
|
_PromptServer.instance = types.SimpleNamespace(routes=routes)
|
|
return routes
|