Fix AI loop hanging: add asyncio.wait_for timeouts on LLM calls

The autonomous conversation loop could hang indefinitely because
self.response_timeout=30 was defined but never used in wait_for().

- autonomous_conversation_controller: wrap generate_persona_response()
  with asyncio.wait_for(timeout=120s); 30s was too short for production
  LLMs, raised to 120s; TimeoutError returns an error dict so the loop
  can continue or count toward consecutive_silence limit
- conversation_decision_service: add asyncio.wait_for(timeout=60s)
  around LLMService.generate_content() for the decision call; add
  asyncio import and explicit TimeoutError handling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-03-23 19:17:36 +00:00
parent 283b31e786
commit 7e72d07329
2 changed files with 30 additions and 16 deletions

View file

@ -32,7 +32,7 @@ class AutonomousConversationController:
# Timing configuration
self.min_delay_between_actions = 3 # seconds
self.max_delay_between_actions = 10 # seconds
self.response_timeout = 30 # seconds
self.response_timeout = 120 # seconds (LLM calls can be slow in production)
# Edge case tracking
self.consecutive_silence_count = 0
@ -670,18 +670,25 @@ class AutonomousConversationController:
messages = await FocusGroup.get_messages(self.focus_group_id)
recent_messages = messages[-20:] if len(messages) > 20 else messages
# Generate response
# Generate response with timeout to prevent infinite hang
try:
response_text = await generate_persona_response(
persona=persona,
current_topic=topic,
previous_messages=recent_messages,
temperature=0.7,
focus_group_id=self.focus_group_id,
llm_model=llm_model,
reasoning_effort=reasoning_effort,
verbosity=verbosity
response_text = await asyncio.wait_for(
generate_persona_response(
persona=persona,
current_topic=topic,
previous_messages=recent_messages,
temperature=0.7,
focus_group_id=self.focus_group_id,
llm_model=llm_model,
reasoning_effort=reasoning_effort,
verbosity=verbosity
),
timeout=self.response_timeout
)
except asyncio.TimeoutError:
error_msg = f"Participant response generation timed out after {self.response_timeout}s"
self.logger.error(f"⏱️ {error_msg}")
return {"error": error_msg}
except Exception as e:
self.logger.error(f"Error in generate_persona_response: {str(e)}")
import traceback

View file

@ -4,6 +4,7 @@ Uses LLM to make intelligent decisions about conversation flow, participant sele
"""
from typing import Dict, Any, Optional, List
import asyncio
import json
from app.services.llm_service import LLMService, LLMServiceError
from app.services.conversation_context_service import ConversationContextService
@ -58,12 +59,15 @@ class ConversationDecisionService:
focus_group = await FocusGroup.find_by_id(focus_group_id)
llm_model = focus_group.get('llm_model') if focus_group else None
# Get LLM decision
# Get LLM decision with timeout to prevent infinite hang
try:
response = await LLMService.generate_content(
prompt=prompt,
temperature=temperature,
model_name=llm_model
response = await asyncio.wait_for(
LLMService.generate_content(
prompt=prompt,
temperature=temperature,
model_name=llm_model
),
timeout=60
)
# Parse the JSON response
@ -92,6 +96,9 @@ class ConversationDecisionService:
return decision
except asyncio.TimeoutError:
print(f"⏱️ LLM decision timed out after 60s")
raise ConversationDecisionError("LLM decision timed out after 60s")
except LLMServiceError as e:
print(f"❌ LLM Service Error: {str(e)}")
raise ConversationDecisionError(f"Error getting LLM decision: {str(e)}")