add rule generalization, sized LLM defaults, ops docs, and UI polish
Feedback rules can now be suggested for merging when a human has already reviewed the same pattern multiple times (never guesses about new values). LLM defaults sized for constrained VRAM instead of llama3.3/llama3.1. Adds systemd units, a state backup script, and reverse-proxy auth guidance for production deployment. Findings view is collapsible and sorts suggested-review rows to the top for a quicker overview.
This commit is contained in:
@@ -3,7 +3,7 @@ import time
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from fgai.feedback import FeedbackStore, feedback_matches, select_match
|
||||
from fgai.feedback import FeedbackStore, feedback_matches, select_match, suggest_generalizations
|
||||
|
||||
|
||||
class FeedbackTests(unittest.TestCase):
|
||||
@@ -120,6 +120,43 @@ class FeedbackTests(unittest.TestCase):
|
||||
self.assertEqual(select_match(candidates), value_rule)
|
||||
self.assertEqual(select_match([stream_rule, entity_rule]), entity_rule)
|
||||
|
||||
def test_suggest_generalizations_groups_narrow_value_rules_by_stream_entity_field_status(self):
|
||||
entries = [
|
||||
{"scope": "value", "status": "false_positive", "entity": "alice", "stream_id": "windows", "field": "username", "value": "bob", "id": "1"},
|
||||
{"scope": "value", "status": "false_positive", "entity": "alice", "stream_id": "windows", "field": "username", "value": "carol", "id": "2"},
|
||||
{"scope": "value", "status": "false_positive", "entity": "alice", "stream_id": "windows", "field": "username", "value": "dave", "id": "3"},
|
||||
{"scope": "value", "status": "confirmed", "entity": "alice", "stream_id": "windows", "field": "username", "value": "eve", "id": "4"},
|
||||
]
|
||||
suggestions = suggest_generalizations(entries, min_values=3)
|
||||
self.assertEqual(len(suggestions), 1)
|
||||
self.assertEqual(suggestions[0]["value_count"], 3)
|
||||
self.assertEqual(suggestions[0]["status"], "false_positive")
|
||||
self.assertEqual(set(suggestions[0]["entry_ids"]), {"1", "2", "3"})
|
||||
|
||||
def test_suggest_generalizations_respects_min_values_threshold(self):
|
||||
entries = [
|
||||
{"scope": "value", "status": "expected", "entity": "a", "stream_id": "s", "field": "f", "value": "x", "id": "1"},
|
||||
{"scope": "value", "status": "expected", "entity": "a", "stream_id": "s", "field": "f", "value": "y", "id": "2"},
|
||||
]
|
||||
self.assertEqual(suggest_generalizations(entries, min_values=3), [])
|
||||
|
||||
def test_suggest_generalizations_ignores_broader_scope_entries(self):
|
||||
entries = [
|
||||
{"scope": "field", "status": "false_positive", "entity": "a", "stream_id": "s", "field": "f", "id": "1"},
|
||||
{"scope": "entity", "status": "expected", "entity": "a", "id": "2"},
|
||||
]
|
||||
self.assertEqual(suggest_generalizations(entries, min_values=2), [])
|
||||
|
||||
def test_suggest_generalizations_keeps_different_entities_separate(self):
|
||||
entries = [
|
||||
{"scope": "value", "status": "false_positive", "entity": "alice", "stream_id": "s", "field": "f", "value": "1", "id": "1"},
|
||||
{"scope": "value", "status": "false_positive", "entity": "alice", "stream_id": "s", "field": "f", "value": "2", "id": "2"},
|
||||
{"scope": "value", "status": "false_positive", "entity": "bob", "stream_id": "s", "field": "f", "value": "3", "id": "3"},
|
||||
]
|
||||
self.assertEqual(suggest_generalizations(entries, min_values=2), [
|
||||
{"stream_id": "s", "entity": "alice", "field": "f", "status": "false_positive", "value_count": 2, "sample_values": ["1", "2"], "entry_ids": ["1", "2"]}
|
||||
])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user