feat: AI auto-fix + Gemini Flash provider #93

Merged
chihlasm merged 14 commits from feat/ai-autofix-gemini into main 2026-02-27 07:32:24 +00:00
2 changed files with 26 additions and 1 deletions
Showing only changes of commit 6fc76187c0 - Show all commits

View File

@@ -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 = (

View File

@@ -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({