fix(m3): AI-assist scope + confidence gate + grouped wood categories

- inference: _taxonomy_lists hands the model the synced species leaves + wood
  leaves + case-insensitive attribute terms (was furniture roots / empty)
- inference: CALL1_SEED pins exact flat schema + *_confidence keys, drops grade
- inference: _gate maps field->confidence key correctly (was species_id_confidence),
  reads nested shape as safety net, omits grade (METADATA-40)
- review.svelte: figure gets AI assist, grade dropped from AI (owner judgment)
- MultiSelect: grouped mode (parents non-selectable headers, selectable leaves)
- METADATA-40 updated: grade not suggested by Call 1
This commit is contained in:
Ty
2026-09-20 21:47:36 -07:00
parent e36979617b
commit cec03c5bd3
5 changed files with 212 additions and 82 deletions
+22
View File
@@ -201,6 +201,28 @@ def test_create_null_sku_allowed(client, db):
assert resp.json()["sku"] is None
def test_gate_reads_flat_confidence_keys(client, db):
# Regression: the gate's confidence keys are 'species_confidence' / 'edge_confidence'
# etc., NOT f'{field}_confidence' (field 'species_id' would give the wrong key). A
# high-confidence response must surface its picks; low-confidence ones must not.
from backend.inference import _gate, _parse_json
good = '```json\n{"species_id": 284, "species_confidence": 0.85, "wood_category_ids": [251], "wood_category_confidence": 1.0, "edge_type_term_id": 323, "edge_confidence": 0.95, "figure_term_ids": [332], "figure_confidence": 0.9, "character": [], "inclusions": ["bark"], "voids": [], "checks": []}\n```'
out = _gate(_parse_json(good))
assert out["species_id"] == 284
assert out["wood_category_ids"] == [251]
assert out["edge_type_term_id"] == 323
assert out["figure_term_ids"] == [332]
# grade must never be gated in (METADATA-40)
assert "grade_term_id" not in out
# low-confidence fields are dropped (METADATA-45)
low = '```json\n{"species_id": 284, "species_confidence": 0.4, "wood_category_ids": [251], "wood_category_confidence": 0.8, "edge_type_term_id": 323, "edge_confidence": 0.2, "figure_term_ids": [332], "figure_confidence": 0.9}\n```'
out2 = _gate(_parse_json(low))
assert out2["wood_category_ids"] == [251]
assert out2["figure_term_ids"] == [332]
assert "species_id" not in out2
assert "edge_type_term_id" not in out2
def test_ready_gate_rejects_malformed_sku(client, db):
# REVIEW-45 enforced in the middle tier: a present-but-out-of-range/char-set SKU is not ready.
from backend.review import compute_ready, sku_valid