diff --git a/backend/app/core/ai_provider.py b/backend/app/core/ai_provider.py index cea16269..cb3f7178 100644 --- a/backend/app/core/ai_provider.py +++ b/backend/app/core/ai_provider.py @@ -5,10 +5,13 @@ Supports Gemini (google-genai) and Anthropic (anthropic) as interchangeable backends for JSON generation used by the AI Flow Builder. """ +import logging from abc import ABC, abstractmethod from app.core.config import settings +logger = logging.getLogger(__name__) + class AIProvider(ABC): """Abstract base class for AI providers.""" @@ -74,6 +77,16 @@ class GeminiProvider(AIProvider): config=config, ) + # Log finish reason to detect truncation + if response.candidates: + finish_reason = getattr(response.candidates[0], "finish_reason", None) + logger.info("Gemini finish_reason=%s model=%s", finish_reason, self._model) + if str(finish_reason) == "MAX_TOKENS": + logger.warning( + "Gemini output truncated (MAX_TOKENS). max_output_tokens=%d", + max_tokens, + ) + text = response.text or "" input_tokens = getattr(response.usage_metadata, "prompt_token_count", 0) or 0 output_tokens = ( diff --git a/backend/app/core/ai_tree_generator_service.py b/backend/app/core/ai_tree_generator_service.py index 7a562d1c..bf560874 100644 --- a/backend/app/core/ai_tree_generator_service.py +++ b/backend/app/core/ai_tree_generator_service.py @@ -154,15 +154,23 @@ async def scaffold_branches( raw_text, input_tokens, output_tokens = await provider.generate_json( system_prompt=SCAFFOLD_SYSTEM_PROMPT, messages=[{"role": "user", "content": user_message}], - max_tokens=1024, + max_tokens=2048, ) + logger.info( + "scaffold raw response (tokens in=%d out=%d, len=%d): %s", + input_tokens, + output_tokens, + len(raw_text), + raw_text[:500], + ) raw_text = _strip_markdown_fences(raw_text) cost = _estimate_cost(input_tokens, output_tokens) try: data = json.loads(raw_text) except json.JSONDecodeError as e: + logger.error("scaffold JSON parse failed. Full text (%d chars): %s", len(raw_text), raw_text) raise ValueError(f"AI returned invalid JSON: {e}") branches = data.get("branches", []) @@ -224,6 +232,10 @@ async def generate_branch_detail( try: branch_tree = json.loads(raw_text) except json.JSONDecodeError as e: + logger.error( + "branch_detail attempt=%d JSON parse failed (%d chars): %s", + attempt, len(raw_text), raw_text[:500], + ) if attempt < 2: messages.append({"role": "assistant", "content": raw_text}) messages.append({