Compare commits

...
Author SHA1 Message Date
Gerald LonlasandClaude Opus 4.7 2301e15111 fix: include both endpoint errors when chat.completions fallback also fails
When the responses endpoint fails and the chat.completions fallback also
fails (raises or returns empty), the original responses error was captured
in fallback_reason but never surfaced. Wrap the fallback call in its own
try/except and re-raise a RuntimeError that chains both errors with
context, so users see why both endpoints failed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 00:46:20 +08:00
4 changed files with 117 additions and 27 deletions
+21 -15
View File
@@ -164,21 +164,27 @@ class LMStudioImageToText(io.ComfyNode):
first_image = image[0] if is_batch else image
image_data_url = comfy_image_to_base64_png_url(first_image)
completion = client.chat.completions.create(
model=connection.model,
messages=build_chat_messages_with_image(
system_prompt=system_prompt,
user_prompt=user_prompt,
image_data_url=image_data_url,
),
seed=resolved_seed,
temperature=connection.temperature,
max_tokens=connection.max_tokens,
n=1,
)
text = extract_chat_completion_text(completion).strip()
if not text:
raise ValueError("chat.completions fallback returned no text output")
try:
completion = client.chat.completions.create(
model=connection.model,
messages=build_chat_messages_with_image(
system_prompt=system_prompt,
user_prompt=user_prompt,
image_data_url=image_data_url,
),
seed=resolved_seed,
temperature=connection.temperature,
max_tokens=connection.max_tokens,
n=1,
)
text = extract_chat_completion_text(completion).strip()
if not text:
raise ValueError("chat.completions fallback returned no text output")
except Exception as chat_error:
raise RuntimeError(
f"Both endpoints failed. responses error: {fallback_reason}. "
f"chat.completions error: {chat_error}"
) from chat_error
if batch_size > 1:
fallback_reason += (
+38
View File
@@ -158,3 +158,41 @@ def test_execute_chat_fallback_reports_dropped_frames(
assert "via chat.completions" in output.ui.text
assert "responses unavailable" in output.ui.text
assert "additional frame(s) were dropped" in output.ui.text
def test_execute_chains_both_errors_when_chat_completions_also_raises(
monkeypatch: pytest.MonkeyPatch,
) -> None:
image_node = _image_module()
def raise_responses(**kwargs):
raise RuntimeError("responses boom")
def raise_chat(**kwargs):
raise RuntimeError("chat boom")
fake_client = SimpleNamespace(
responses=SimpleNamespace(create=raise_responses),
chat=SimpleNamespace(completions=SimpleNamespace(create=raise_chat)),
)
monkeypatch.setattr(image_node, "resolve_request_seed", lambda _: 41)
monkeypatch.setattr(image_node, "create_openai_client", lambda **_: fake_client)
monkeypatch.setattr(
image_node, "comfy_image_to_base64_png_url", lambda _img: "data:image/png;base64,xxx"
)
with pytest.raises(RuntimeError) as exc_info:
image_node.LMStudioImageToText.execute(
connection=_connection_payload(),
image=_single_image(),
system_prompt="sys",
user_prompt="describe",
seed=0,
)
message = str(exc_info.value)
assert "Both endpoints failed" in message
assert "responses boom" in message
assert "chat boom" in message
assert isinstance(exc_info.value.__cause__, RuntimeError)
assert str(exc_info.value.__cause__) == "chat boom"
+41 -1
View File
@@ -161,10 +161,50 @@ def test_execute_raises_if_both_endpoints_return_no_text(
monkeypatch.setattr(text_gen_node, "create_openai_client", lambda **_: fake_client)
monkeypatch.setattr(text_gen_node, "resolve_request_seed", lambda _: 55)
with pytest.raises(ValueError, match="chat\\.completions fallback returned no text output"):
with pytest.raises(RuntimeError) as exc_info:
text_gen_node.LMStudioTextGen.execute(
connection=_connection_payload(),
system_prompt="sys",
user_prompt="hello",
seed=1,
)
message = str(exc_info.value)
assert "Both endpoints failed" in message
assert "responses endpoint returned no text output" in message
assert "chat.completions fallback returned no text output" in message
def test_execute_chains_both_errors_when_chat_completions_also_raises(
monkeypatch: pytest.MonkeyPatch,
) -> None:
text_gen_node = _text_module()
def raise_responses(**kwargs):
raise RuntimeError("responses boom")
def raise_chat(**kwargs):
raise RuntimeError("chat boom")
fake_client = SimpleNamespace(
responses=SimpleNamespace(create=raise_responses),
chat=SimpleNamespace(completions=SimpleNamespace(create=raise_chat)),
)
monkeypatch.setattr(text_gen_node, "create_openai_client", lambda **_: fake_client)
monkeypatch.setattr(text_gen_node, "resolve_request_seed", lambda _: 55)
with pytest.raises(RuntimeError) as exc_info:
text_gen_node.LMStudioTextGen.execute(
connection=_connection_payload(),
system_prompt="sys",
user_prompt="hello",
seed=1,
)
message = str(exc_info.value)
assert "Both endpoints failed" in message
assert "responses boom" in message
assert "chat boom" in message
assert isinstance(exc_info.value.__cause__, RuntimeError)
assert str(exc_info.value.__cause__) == "chat boom"
+17 -11
View File
@@ -136,17 +136,23 @@ class LMStudioTextGen(io.ComfyNode):
via_endpoint = "chat.completions"
fallback_reason = str(responses_error)
completion = client.chat.completions.create(
model=connection.model,
messages=build_chat_messages(system_prompt, user_prompt),
seed=resolved_seed,
temperature=connection.temperature,
max_tokens=connection.max_tokens,
n=1,
)
text = extract_chat_completion_text(completion).strip()
if not text:
raise ValueError("chat.completions fallback returned no text output")
try:
completion = client.chat.completions.create(
model=connection.model,
messages=build_chat_messages(system_prompt, user_prompt),
seed=resolved_seed,
temperature=connection.temperature,
max_tokens=connection.max_tokens,
n=1,
)
text = extract_chat_completion_text(completion).strip()
if not text:
raise ValueError("chat.completions fallback returned no text output")
except Exception as chat_error:
raise RuntimeError(
f"Both endpoints failed. responses error: {fallback_reason}. "
f"chat.completions error: {chat_error}"
) from chat_error
text = strip_think_content(text)