add value-shape field classification and auto-apply

Field roles are now inferred from observed values instead of names,
with src/dst disambiguation, value-pattern detector hints, an Ollama
advisor scoped to ambiguous fields only, confidence-gated auto-apply
with manual-field protection, and drift flagging for review
This commit is contained in:
larssand
2026-09-07 21:58:25 +02:00
parent 7b94512abc
commit e35445da4f
22 changed files with 1133 additions and 93 deletions

71
tests/test_history.py Normal file
View File

@@ -0,0 +1,71 @@
import tempfile
import time
import unittest
from pathlib import Path
from fgai.history import FieldDiscoveryStore
class FieldDiscoveryConfidenceHistoryTests(unittest.TestCase):
def test_record_and_read_applied_confidence(self):
with tempfile.TemporaryDirectory() as tmp:
store = FieldDiscoveryStore(str(Path(tmp) / "history.sqlite3"))
store.record_applied_confidence(
"custom_vendor",
["x7_field_a", "x7_field_b"],
field_confidence={"x7_field_a": 85, "x7_field_b": 40},
field_shapes={"x7_field_a": "ip", "x7_field_b": "categorical_low_cardinality"},
)
applied = store.applied_confidence("custom_vendor")
self.assertEqual(applied["x7_field_a"]["applied_confidence"], 85)
self.assertEqual(applied["x7_field_a"]["applied_shape"], "ip")
self.assertEqual(applied["x7_field_b"]["applied_confidence"], 40)
def test_record_ignores_fields_without_confidence_entry(self):
with tempfile.TemporaryDirectory() as tmp:
store = FieldDiscoveryStore(str(Path(tmp) / "history.sqlite3"))
store.record_applied_confidence(
"custom_vendor",
["x7_field_a", "timestamp"],
field_confidence={"x7_field_a": 85},
field_shapes={"x7_field_a": "ip"},
)
applied = store.applied_confidence("custom_vendor")
self.assertIn("x7_field_a", applied)
self.assertNotIn("timestamp", applied)
def test_record_keeps_first_snapshot_and_ignores_later_calls(self):
with tempfile.TemporaryDirectory() as tmp:
store = FieldDiscoveryStore(str(Path(tmp) / "history.sqlite3"))
store.record_applied_confidence("custom_vendor", ["x7_field_a"], {"x7_field_a": 65}, {"x7_field_a": "ip"})
store.record_applied_confidence("custom_vendor", ["x7_field_a"], {"x7_field_a": 90}, {"x7_field_a": "ip"})
applied = store.applied_confidence("custom_vendor")
self.assertEqual(applied["x7_field_a"]["applied_confidence"], 65)
self.assertEqual(len(applied), 1)
def test_confidence_history_maintenance_prunes_old_rows(self):
with tempfile.TemporaryDirectory() as tmp:
db_path = Path(tmp) / "history.sqlite3"
store = FieldDiscoveryStore(str(db_path))
store.record_applied_confidence("custom_vendor", ["x7_field_a"], {"x7_field_a": 85}, {"x7_field_a": "ip"})
import sqlite3
old_time = int(time.time()) - 200 * 86400
with sqlite3.connect(db_path) as connection:
connection.execute("update field_confidence_history set applied_at = ?", (old_time,))
result = store.confidence_history_maintenance(retention_days=90)
self.assertEqual(result["removed"], 1)
self.assertEqual(result["remaining"], 0)
self.assertEqual(store.applied_confidence("custom_vendor"), {})
if __name__ == "__main__":
unittest.main()