Coverage for apps / ai / services / timer.py: 31%

26 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-14 19:13 +0000

1"""Timer naming service using AI.""" 

2 

3import logging 

4 

5from ..models import AIPrompt 

6from .cache import cache_ai_response, CACHE_TIMEOUT_SHORT 

7from .openrouter import OpenRouterService 

8from .validator import AIResponseValidator 

9 

10logger = logging.getLogger(__name__) 

11 

12 

13@cache_ai_response("timer_name", timeout=CACHE_TIMEOUT_SHORT) 

14def generate_timer_name(step_text: str, duration_minutes: int) -> dict: 

15 """Generate a descriptive name for a cooking timer. 

16 

17 Args: 

18 step_text: The cooking instruction text. 

19 duration_minutes: The timer duration in minutes. 

20 

21 Returns: 

22 Dict with the generated label. 

23 

24 Raises: 

25 AIUnavailableError: If AI service is not available. 

26 AIResponseError: If AI returns invalid response. 

27 ValidationError: If response doesn't match expected schema. 

28 """ 

29 # Get the timer_naming prompt 

30 prompt = AIPrompt.get_prompt("timer_naming") 

31 

32 # Format duration nicely 

33 if duration_minutes >= 60: 

34 hours = duration_minutes // 60 

35 mins = duration_minutes % 60 

36 if mins > 0: 

37 duration_str = f"{hours} hour{'s' if hours > 1 else ''} {mins} minute{'s' if mins > 1 else ''}" 

38 else: 

39 duration_str = f"{hours} hour{'s' if hours > 1 else ''}" 

40 else: 

41 duration_str = f"{duration_minutes} minute{'s' if duration_minutes > 1 else ''}" 

42 

43 # Format the user prompt 

44 user_prompt = prompt.format_user_prompt( 

45 instruction=step_text, 

46 duration=duration_str, 

47 ) 

48 

49 # Call AI service 

50 service = OpenRouterService() 

51 response = service.complete( 

52 system_prompt=prompt.system_prompt, 

53 user_prompt=user_prompt, 

54 model=prompt.model, 

55 json_response=True, 

56 ) 

57 

58 # Validate response 

59 validator = AIResponseValidator() 

60 result = validator.validate("timer_naming", response) 

61 

62 # Truncate label if too long (max 30 chars as per spec) 

63 label = result["label"] 

64 if len(label) > 30: 

65 label = label[:27] + "..." 

66 

67 logger.info(f'Generated timer name: "{label}" for {duration_minutes}min timer') 

68 

69 return { 

70 "label": label, 

71 } 

← Back to Dashboard