fix: add diagnostic logging and increase scaffold max_tokens to 2048
The "Unterminated string" JSON parse error is likely caused by Gemini output truncation at 1024 tokens. Increases scaffold max_tokens to 2048 and adds logging for: raw response text, finish_reason (truncation detection), and JSON parse failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit was merged in pull request #93.
This commit is contained in:
@@ -5,10 +5,13 @@ Supports Gemini (google-genai) and Anthropic (anthropic) as interchangeable
|
|||||||
backends for JSON generation used by the AI Flow Builder.
|
backends for JSON generation used by the AI Flow Builder.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AIProvider(ABC):
|
class AIProvider(ABC):
|
||||||
"""Abstract base class for AI providers."""
|
"""Abstract base class for AI providers."""
|
||||||
@@ -74,6 +77,16 @@ class GeminiProvider(AIProvider):
|
|||||||
config=config,
|
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 ""
|
text = response.text or ""
|
||||||
input_tokens = getattr(response.usage_metadata, "prompt_token_count", 0) or 0
|
input_tokens = getattr(response.usage_metadata, "prompt_token_count", 0) or 0
|
||||||
output_tokens = (
|
output_tokens = (
|
||||||
|
|||||||
@@ -154,15 +154,23 @@ async def scaffold_branches(
|
|||||||
raw_text, input_tokens, output_tokens = await provider.generate_json(
|
raw_text, input_tokens, output_tokens = await provider.generate_json(
|
||||||
system_prompt=SCAFFOLD_SYSTEM_PROMPT,
|
system_prompt=SCAFFOLD_SYSTEM_PROMPT,
|
||||||
messages=[{"role": "user", "content": user_message}],
|
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)
|
raw_text = _strip_markdown_fences(raw_text)
|
||||||
cost = _estimate_cost(input_tokens, output_tokens)
|
cost = _estimate_cost(input_tokens, output_tokens)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(raw_text)
|
data = json.loads(raw_text)
|
||||||
except json.JSONDecodeError as e:
|
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}")
|
raise ValueError(f"AI returned invalid JSON: {e}")
|
||||||
|
|
||||||
branches = data.get("branches", [])
|
branches = data.get("branches", [])
|
||||||
@@ -224,6 +232,10 @@ async def generate_branch_detail(
|
|||||||
try:
|
try:
|
||||||
branch_tree = json.loads(raw_text)
|
branch_tree = json.loads(raw_text)
|
||||||
except json.JSONDecodeError as e:
|
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:
|
if attempt < 2:
|
||||||
messages.append({"role": "assistant", "content": raw_text})
|
messages.append({"role": "assistant", "content": raw_text})
|
||||||
messages.append({
|
messages.append({
|
||||||
|
|||||||
Reference in New Issue
Block a user