Add content fallback for conversation analysis + debug logging

When the LLM returns text instead of a tool call for conversation
analysis, try parsing the content as JSON before giving up. Also
log what the model actually returns on failure for debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 10:16:15 -05:00
parent 90b70cad69
commit f02a4ab49d

View File

@@ -478,7 +478,17 @@ class LLMClient:
output_tokens=usage.completion_tokens if usage else None)
return self._validate_conversation_result(args)
logger.warning("No tool call in conversation analysis response.")
# Fallback: try parsing the message content as JSON
if choice.message.content:
logger.warning("No tool call in conversation analysis — trying content fallback. Content: %s", choice.message.content[:300])
fallback = self._parse_conversation_content_fallback(choice.message.content)
if fallback:
self._log_llm("conversation", elapsed, True, req_json, choice.message.content,
input_tokens=usage.prompt_tokens if usage else None,
output_tokens=usage.completion_tokens if usage else None)
return fallback
logger.warning("No tool call or parseable content in conversation analysis response.")
self._log_llm("conversation", elapsed, False, req_json, error="Empty response")
return None
@@ -512,6 +522,31 @@ class LLMClient:
result.setdefault("conversation_summary", "")
return result
def _parse_conversation_content_fallback(self, text: str) -> dict | None:
"""Try to parse plain-text content as JSON for conversation analysis."""
import re
# Try direct JSON parse
try:
result = json.loads(text.strip())
if "user_findings" in result:
return self._validate_conversation_result(result)
except (json.JSONDecodeError, ValueError):
pass
# Try extracting from code block
match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL)
if match:
try:
result = json.loads(match.group(1))
if "user_findings" in result:
return self._validate_conversation_result(result)
except (json.JSONDecodeError, ValueError):
pass
logger.warning("Could not parse conversation content fallback: %s", text[:200])
return None
async def chat(
self, messages: list[dict[str, str]], system_prompt: str,
on_first_token=None, recent_bot_replies: list[str] | None = None,