Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions hermes_cli/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -869,10 +869,7 @@ def generate_launchd_plist() -> str:
<true/>

<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<true/>

<key>StandardOutPath</key>
<string>{log_dir}/gateway.log</string>
Expand Down
31 changes: 27 additions & 4 deletions run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7917,21 +7917,44 @@ def _stop_spinner():
self._response_was_previewed = True
break

# No fallback available — this is a genuine empty response.
# No fallback available — check if the model used a
# structured reasoning field (reasoning_content) for its
# answer. Thinking models (Kimi K2.5, DeepSeek-R1, etc.)
# consistently put their full response there and leave
# content empty. Retrying won't change that behavior —
# use the reasoning immediately instead of wasting API calls.
reasoning_text = self._extract_reasoning(assistant_message)
if reasoning_text and (
getattr(assistant_message, 'reasoning_content', None)
or getattr(assistant_message, 'reasoning', None)
):
self._vprint(f"{self.log_prefix}ℹ️ Thinking model returned response in reasoning field — using directly.")
final_response = reasoning_text
empty_msg = {
"role": "assistant",
"content": final_response,
"reasoning": reasoning_text,
"finish_reason": finish_reason,
}
messages.append(empty_msg)
if hasattr(self, '_empty_content_retries'):
self._empty_content_retries = 0
break

# Genuine empty response (no structured reasoning field).
# Retry in case the model just had a bad generation.
if not hasattr(self, '_empty_content_retries'):
self._empty_content_retries = 0
self._empty_content_retries += 1

reasoning_text = self._extract_reasoning(assistant_message)

self._vprint(f"{self.log_prefix}⚠️ Response only contains think block with no content after it")
if reasoning_text:
reasoning_preview = reasoning_text[:500] + "..." if len(reasoning_text) > 500 else reasoning_text
self._vprint(f"{self.log_prefix} Reasoning: {reasoning_preview}")
else:
content_preview = final_response[:80] + "..." if len(final_response) > 80 else final_response
self._vprint(f"{self.log_prefix} Content: '{content_preview}'")

if self._empty_content_retries < 3:
self._vprint(f"{self.log_prefix}🔄 Retrying API call ({self._empty_content_retries}/3)...")
continue
Expand Down