feat(suggestions): tag-input dropdown searches the full prediction set
The typed dropdown sourced the threshold-filtered panel list (>= 0.70 general), so low-confidence actions/features the model DID predict never appeared — forcing hand-typed custom tags instead of accepting the model's canonical formatting. Add a threshold override: SuggestionService.for_image(threshold_override=) and GET /images/<id>/suggestions?min=<f> surface EVERY stored prediction (down to the 0.05 store floor), alias-resolved and normalized, still excluding applied/rejected and unsurfaced categories. The suggestions store gains allByCategory + loadAll (min=0); the dropdown searches that full set (cap 20), while the Suggestions panel stays curated at the configured threshold. Accept/dismiss drop from both lists. Operator-asked 2026-06-09. Test: a 0.30 general prediction is hidden by default but surfaced with threshold_override=0.0; unsurfaced categories still excluded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,7 +62,11 @@ const isEmpty = computed(() =>
|
||||
Object.values(store.byCategory).every(list => !list || list.length === 0)
|
||||
)
|
||||
|
||||
watch(() => props.imageId, (id) => { if (id != null) store.load(id) }, { immediate: true })
|
||||
watch(() => props.imageId, (id) => {
|
||||
if (id == null) return
|
||||
store.load(id) // panel: curated, ≥ threshold
|
||||
store.loadAll(id) // dropdown: full prediction set (low-confidence included)
|
||||
}, { immediate: true })
|
||||
|
||||
// After a successful accept/alias-accept, refresh the modal's current
|
||||
// tag list so TagPanel's chip rail reflects the newly-attached tag.
|
||||
|
||||
@@ -190,12 +190,17 @@ function scorePct (s) { return `${Math.round(s.score * 100)}%` }
|
||||
|
||||
// This image's suggestions that match the typed query, minus any the server
|
||||
// autocomplete already returned (same name+kind) so a tag never shows twice.
|
||||
// Sources the FULL prediction set (allByCategory, down to the store floor) — NOT
|
||||
// the threshold-filtered panel list — so a low-confidence action/feature the
|
||||
// model saw can be typed and accepted in canonical formatting instead of being
|
||||
// hand-entered as a custom tag (operator-asked 2026-06-09). The typed query is
|
||||
// the only filter; the threshold no longer hides anything here.
|
||||
const suggestionHits = computed(() => {
|
||||
const q = parsedName.value.toLowerCase()
|
||||
if (!q) return []
|
||||
const seen = new Set(hits.value.map(h => `${h.kind}:${h.name.toLowerCase()}`))
|
||||
const out = []
|
||||
for (const list of Object.values(suggestions.byCategory)) {
|
||||
for (const list of Object.values(suggestions.allByCategory)) {
|
||||
for (const s of list || []) {
|
||||
const key = `${s.category}:${s.display_name.toLowerCase()}`
|
||||
if (!s.display_name.toLowerCase().includes(q)) continue
|
||||
@@ -204,9 +209,10 @@ const suggestionHits = computed(() => {
|
||||
out.push(s)
|
||||
}
|
||||
}
|
||||
// Best matches first; cap so the dropdown stays scannable.
|
||||
// Best matches first; cap generously so a specific typed query surfaces its
|
||||
// matches even when many predictions exist, while the list stays scrollable.
|
||||
out.sort((a, b) => b.score - a.score)
|
||||
return out.slice(0, 6)
|
||||
return out.slice(0, 20)
|
||||
})
|
||||
|
||||
// One ordered list backing both the rendered dropdown and keyboard nav, so the
|
||||
|
||||
Reference in New Issue
Block a user