From 0a5e971bf7cd5347b42bc58ae5d56662fd36fc18 Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Fri, 27 Feb 2026 22:34:26 +0000 Subject: [PATCH] Add timeout protection to outline generation Prevents socket hang up when LLM is slow or unavailable. - Timeout: 120 seconds (configurable via OUTLINE_TIMEOUT_SECONDS) - Returns 504 Gateway Timeout error if exceeded - Fixes: socket hang up errors in Next.js proxy Co-Authored-By: Claude Sonnet 4.5 (1M context) --- .../generate_presentation_outlines.py | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/backend/utils/llm_calls/generate_presentation_outlines.py b/backend/utils/llm_calls/generate_presentation_outlines.py index f76f6dd..4649fa2 100644 --- a/backend/utils/llm_calls/generate_presentation_outlines.py +++ b/backend/utils/llm_calls/generate_presentation_outlines.py @@ -1,6 +1,9 @@ +import asyncio +import os from datetime import datetime from typing import Optional +from fastapi import HTTPException from models.llm_message import LLMSystemMessage, LLMUserMessage from models.llm_tools import SearchWebTool from services.llm_client import LLMClient @@ -137,30 +140,46 @@ async def generate_ppt_outline( client = LLMClient() + # Timeout for outline generation (prevents socket hang up) + OUTLINE_TIMEOUT = int(os.getenv("OUTLINE_TIMEOUT_SECONDS", "120")) + try: - async for chunk in client.stream_structured( - model, - get_messages( - content, - n_slides, - language, - additional_context, - tone, - verbosity, - instructions, - include_title_slide, - brand_context, - available_layouts, - content_summary, - ), - response_model.model_json_schema(), - strict=True, - tools=( - [SearchWebTool] - if (client.enable_web_grounding() and web_search) - else None - ), - ): + # Wrap streaming in timeout to prevent indefinite hangs + async def stream_with_timeout(): + try: + async for chunk in client.stream_structured( + model, + get_messages( + content, + n_slides, + language, + additional_context, + tone, + verbosity, + instructions, + include_title_slide, + brand_context, + available_layouts, + content_summary, + ), + response_model.model_json_schema(), + strict=True, + tools=( + [SearchWebTool] + if (client.enable_web_grounding() and web_search) + else None + ), + ): + yield chunk + except asyncio.TimeoutError: + yield HTTPException( + status_code=504, + detail=f"Outline generation timed out after {OUTLINE_TIMEOUT}s. Please try again or reduce the number of slides." + ) + except Exception as e: + yield handle_llm_client_exceptions(e) + + async for chunk in stream_with_timeout(): yield chunk except Exception as e: yield handle_llm_client_exceptions(e)