diff --git a/utils/llm_client.py b/utils/llm_client.py index 85103d0..8108cb1 100644 --- a/utils/llm_client.py +++ b/utils/llm_client.py @@ -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,