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
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-14 19:13 +0000
1"""Timer naming service using AI."""
3import logging
5from ..models import AIPrompt
6from .cache import cache_ai_response, CACHE_TIMEOUT_SHORT
7from .openrouter import OpenRouterService
8from .validator import AIResponseValidator
10logger = logging.getLogger(__name__)
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.
17 Args:
18 step_text: The cooking instruction text.
19 duration_minutes: The timer duration in minutes.
21 Returns:
22 Dict with the generated label.
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")
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 ''}"
43 # Format the user prompt
44 user_prompt = prompt.format_user_prompt(
45 instruction=step_text,
46 duration=duration_str,
47 )
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 )
58 # Validate response
59 validator = AIResponseValidator()
60 result = validator.validate("timer_naming", response)
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] + "..."
67 logger.info(f'Generated timer name: "{label}" for {duration_minutes}min timer')
69 return {
70 "label": label,
71 }