diff --git a/agent_service/agents/master_agent.py b/agent_service/agents/master_agent.py index 8af8eec..eba946d 100644 --- a/agent_service/agents/master_agent.py +++ b/agent_service/agents/master_agent.py @@ -147,23 +147,37 @@ class MasterAgent: msgs = [{'role': 'system', 'content': system}, *history, {'role': 'user', 'content': message}] resp = await self._llm.submit(msgs, caller='master') + raw = (resp.content or '').strip() + # Strip markdown fences like ```json ... ``` + if raw.startswith('```'): + raw = raw.strip('`') + if raw.lower().startswith('json'): + raw = raw[4:] + raw = raw.strip() + # Pull out the first {...} block if there's surrounding prose. + first = raw.find('{') + last = raw.rfind('}') + if first != -1 and last != -1 and last > first: + raw = raw[first:last + 1] try: - raw = resp.content.strip() - if raw.startswith(chr(96)*3): - raw = raw.split(chr(10), 1)[1].rsplit(chr(10), 1)[0] data = json.loads(raw) + if not isinstance(data, dict): + raise ValueError(f'expected JSON object, got {type(data).__name__}') return IntentResult( - needs_clarification=data.get('needs_clarification', False), + needs_clarification=bool(data.get('needs_clarification', False)), clarification_question=data.get('clarification_question'), - is_continuation=data.get('is_continuation', False), - agents=data.get('agents', []), - intent_summary=data.get('intent_summary', ''), - params=data.get('params', {}), - context_hints=data.get('context_hints', [])) - except (json.JSONDecodeError, KeyError) as exc: - logger.warning('Intent classification parse failed: %s', exc) - return IntentResult(needs_clarification=True, - clarification_question='Could you clarify what you need?') + is_continuation=bool(data.get('is_continuation', False)), + agents=data.get('agents') or [], + intent_summary=data.get('intent_summary', '') or '', + params=data.get('params') or {}, + context_hints=data.get('context_hints') or []) + except Exception as exc: + logger.warning('Intent classification parse failed (%s); raw=%r', + exc, (resp.content or '')[:300]) + # Treat unparseable LLM output as 'no specialist agent applies' so + # the direct-answer fallback can handle it instead of looping on + # clarification. + return IntentResult(needs_clarification=False, agents=[]) async def _check_access(self, user_id, agents) -> AccessResult: denied = []