"""Tests for GenAI prompt builders.""" import unittest from frigate.config.camera.review import ReviewResponseStyleEnum from frigate.genai.prompts import ( REVIEW_DESCRIPTION_FIELD_GUIDELINES, REVIEW_RESPONSE_STYLES, build_review_description_prompt, build_review_summary_prompt, get_review_field_guidelines, ) class TestReviewResponseStyle(unittest.TestCase): def _build_prompt(self, response_style: str = "default") -> str: review_data = { "camera": "Front Door", "start": "Monday, 09:30 AM", "duration": 25, "zones": [], "unified_objects": ["person"], } return build_review_description_prompt( review_data, [b"fake-image"], [], None, "activity context", response_style, ) def test_default_style_leaves_prompt_unchanged(self): self.assertEqual( get_review_field_guidelines("default"), REVIEW_DESCRIPTION_FIELD_GUIDELINES, ) self.assertEqual(self._build_prompt("default"), self._build_prompt()) def test_unknown_style_leaves_prompt_unchanged(self): self.assertEqual(self._build_prompt("unknown"), self._build_prompt()) def test_styles_replace_user_facing_field_guidance(self): default_prompt = self._build_prompt() for style, overrides in REVIEW_RESPONSE_STYLES.items(): prompt = self._build_prompt(style) for field_name, guidance in overrides.items(): self.assertIn(f"- `{field_name}`: {guidance}", prompt) self.assertNotIn( REVIEW_DESCRIPTION_FIELD_GUIDELINES[field_name], prompt ) # Everything outside the overridden guidance lines is unchanged self.assertEqual( [ line for line in prompt.splitlines() if not any(line.startswith(f"- `{f}`:") for f in overrides) ], [ line for line in default_prompt.splitlines() if not any(line.startswith(f"- `{f}`:") for f in overrides) ], ) def test_styles_never_touch_reasoning_or_threat_fields(self): # observations is a reasoning scaffold and potential_threat_level is # scoring guidance; presets restyle only the user-facing fields. for overrides in REVIEW_RESPONSE_STYLES.values(): self.assertTrue( set(overrides) <= {"scene", "title", "shortSummary"}, f"unexpected override fields: {set(overrides)}", ) def test_config_enum_matches_style_presets(self): enum_styles = {e.value for e in ReviewResponseStyleEnum} self.assertEqual(enum_styles, {"default", *REVIEW_RESPONSE_STYLES}) def test_enum_value_selects_preset(self): prompt = self._build_prompt(ReviewResponseStyleEnum.natural) self.assertIn( REVIEW_RESPONSE_STYLES["natural"]["shortSummary"], prompt, ) class TestClassificationStateChanges(unittest.TestCase): def _build_prompt(self, **extra) -> str: review_data = { "camera": "Front Door", "start": "Monday, 09:30 AM", "duration": 25, "zones": [], "unified_objects": ["person"], **extra, } return build_review_description_prompt( review_data, [b"fake-image"], [], None, "activity context" ) def test_no_changes_leaves_prompt_unchanged(self): self.assertEqual( self._build_prompt(classification_state_changes=[]), self._build_prompt(), ) self.assertNotIn("## State Changes", self._build_prompt()) def test_changes_are_listed_before_objects(self): change = "front gate changed from closed to open, 12s into the activity" prompt = self._build_prompt(classification_state_changes=[change]) self.assertIn(f"\n- {change}\n\n## Objects in Scene", prompt) self.assertLess( prompt.index("## Sequence Details"), prompt.index("## State Changes") ) def test_summary_describes_state_changes_only_when_present(self): event = {"title": "Person at door", "camera": "Front Door", "context": []} without = build_review_summary_prompt(0, 3600, [event], None) self.assertNotIn('"state_changes"', without) context_event = { **event, "context": [ {"camera": "Driveway", "state_changes": ["gate changed from a to b"]} ], } with_changes = build_review_summary_prompt(0, 3600, [context_event], None) self.assertIn('- "state_changes"', with_changes) if __name__ == "__main__": unittest.main()