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:
+36
-1
@@ -478,7 +478,17 @@ class LLMClient:
|
|||||||
output_tokens=usage.completion_tokens if usage else None)
|
output_tokens=usage.completion_tokens if usage else None)
|
||||||
return self._validate_conversation_result(args)
|
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")
|
self._log_llm("conversation", elapsed, False, req_json, error="Empty response")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -512,6 +522,31 @@ class LLMClient:
|
|||||||
result.setdefault("conversation_summary", "")
|
result.setdefault("conversation_summary", "")
|
||||||
return result
|
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(
|
async def chat(
|
||||||
self, messages: list[dict[str, str]], system_prompt: str,
|
self, messages: list[dict[str, str]], system_prompt: str,
|
||||||
on_first_token=None, recent_bot_replies: list[str] | None = None,
|
on_first_token=None, recent_bot_replies: list[str] | None = None,
|
||||||
|
|||||||
Reference in New Issue
Block a user