refactor: consolidate LLM JSON parsing into shared llm_utils module
Extracted duplicate _strip_markdown_fences / _parse_llm_json functions from 7 files into app/services/llm_utils.py. Two shared functions: - strip_markdown_fences(): fence stripping only - parse_llm_json(): fence stripping + JSON parse + error logging Files updated: flowpilot_engine, knowledge_flywheel, session_to_flow_service, ai_tree_generator_service, ai_fix_service, ai_chat_service, kb_conversion_service Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.core.ai_provider import get_ai_provider
|
||||
from app.core.config import settings
|
||||
from app.services.llm_utils import parse_llm_json
|
||||
from app.services.notification_service import notify
|
||||
from app.models.ai_session import AISession
|
||||
from app.models.ai_session_step import AISessionStep
|
||||
@@ -316,7 +317,7 @@ async def _propose_new_flow(session: AISession, db: AsyncSession) -> None:
|
||||
max_tokens=4096,
|
||||
)
|
||||
|
||||
parsed = _parse_llm_json(raw_response)
|
||||
parsed = parse_llm_json(raw_response)
|
||||
except Exception as e:
|
||||
logger.warning("Knowledge Flywheel LLM call failed for session %s: %s", session.id, e)
|
||||
return
|
||||
@@ -407,7 +408,7 @@ async def _propose_enhancement(session: AISession, db: AsyncSession) -> None:
|
||||
max_tokens=4096,
|
||||
)
|
||||
|
||||
parsed = _parse_llm_json(raw_response)
|
||||
parsed = parse_llm_json(raw_response)
|
||||
except Exception as e:
|
||||
logger.warning("Knowledge Flywheel enhancement LLM call failed for session %s: %s", session.id, e)
|
||||
return
|
||||
@@ -451,18 +452,3 @@ async def _propose_enhancement(session: AISession, db: AsyncSession) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _parse_llm_json(raw_text: str) -> dict[str, Any]:
|
||||
"""Parse JSON from LLM response, handling common quirks."""
|
||||
text = raw_text.strip()
|
||||
|
||||
# Strip markdown code fences if present
|
||||
if text.startswith("```"):
|
||||
lines = text.split("\n")
|
||||
lines = [line for line in lines if not line.strip().startswith("```")]
|
||||
text = "\n".join(lines).strip()
|
||||
|
||||
try:
|
||||
return json.loads(text)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.warning("Knowledge Flywheel JSON parse failed: %s — raw: %.300s", e, text)
|
||||
raise ValueError(f"Invalid JSON from LLM: {e}") from e
|
||||
|
||||
Reference in New Issue
Block a user