Spaces:
Running
Running
| /** | |
| * Intent parser: maps natural language to task types. | |
| * Ported from hf_space/intent_parser.py. | |
| */ | |
| import { DEFAULT_DETECT_CLASSES, KEYWORD_FALLBACK, ROUTING_GROUP_MAP } from "./constants.js"; | |
| const INTERPRET_KW = ["interpret","analyze","analyse","describe","assessment","what do you see","what is this","clinical interpretation","examine","evaluate","tell me about"]; | |
| const DETECT_KW = ["detect","find","locate","where is","where are","bounding box","identify","show me","mark","highlight"]; | |
| const SEGMENT_KW = ["segment","outline","contour","mask","delineate","boundary","trace","polygon"]; | |
| const CLASSIFY_KW = ["classify","what type","what view","which plane","what kind","categorize","classification"]; | |
| const GREETING_KW = ["hello","hi ","hey","good morning","good afternoon","thanks","thank you"]; | |
| const ANATOMY_TO_GROUP = new Map([ | |
| ["nt keypoint","nt_kp"],["nt kp","nt_kp"],["nuchal keypoint","nt_kp"],["ntkpoint","nt_kp"], | |
| ["brain keypoint","brain"],["cardiac keypoint","cardiac"], | |
| ["fetal head","pelvimetry"],["fetal_head","pelvimetry"],["angle of progression","pelvimetry"], | |
| ["keypoint","crl_kp"],["kpoint","crl_kp"],["scalebar","crl_kp"],["scale bar","crl_kp"], | |
| ["crl_kp","crl_kp"],["scalebarpoint","crl_kp"], | |
| ["brain","brain"],["cerebral","brain"],["cerebellum","brain"],["ventricle","brain"], | |
| ["thalami","brain"],["bpd","brain"],["head circumference","brain"],["csp","brain"], | |
| ["cavum","brain"],["septum pellucidum","brain"],["lv","brain"],["lateral ventricle","brain"], | |
| ["choroid plexus","brain"],["falx","brain"], | |
| ["heart","cardiac"],["cardiac","cardiac"],["thorax","cardiac"],["thoracic","cardiac"], | |
| ["aorta","cardiac"],["four chamber","cardiac"],["cardic","cardiac"],["cariac","cardiac"], | |
| ["nuchal","nt_nasal"],["nasal","nt_nasal"],["translucency","nt_nasal"],["nasal bone","nt_nasal"], | |
| ["nasal tip","nt_nasal"],["nasal skin","nt_nasal"],["nuchel","nt_nasal"],["translucen","nt_nasal"], | |
| ["crown-rump","crl"],["crown rump","crl"],["crl","crl"], | |
| ["doppler","doppler"],["flow","doppler"],["vessel","doppler"],["artery","doppler"], | |
| ["vein","doppler"],["umbilical","doppler"], | |
| ["cervix","pelvimetry"],["pelvimetry","pelvimetry"],["symphysis","pelvimetry"], | |
| ["pubic","pelvimetry"],["symphsis","pelvimetry"],["symphis","pelvimetry"],["pelvimtry","pelvimetry"], | |
| ["body","body_pose"],["abdomen","body_pose"],["arm","body_pose"],["legs","body_pose"], | |
| ["pose","body_pose"],["abdomin","body_pose"], | |
| ["femur","femur"],["thigh","femur"],["femor","femur"], | |
| ]); | |
| export function parseIntent(userText, hasImage = false, hasInterpretation = false) { | |
| const text = userText.trim().toLowerCase(); | |
| if (!text && hasImage) return { task: "interpret" }; | |
| for (const kw of GREETING_KW) { | |
| if (text.startsWith(kw) || text.includes(` ${kw}`)) { | |
| if (hasImage && !hasInterpretation) return { task: "interpret" }; | |
| return { task: "greeting" }; | |
| } | |
| } | |
| for (const kw of SEGMENT_KW) { | |
| if (text.includes(kw)) { | |
| const { det, seg } = extractClasses(text); | |
| return { task: "segment", classes: det || seg, segClasses: seg }; | |
| } | |
| } | |
| for (const kw of DETECT_KW) { | |
| if (text.includes(kw)) { | |
| const { det } = extractClasses(text); | |
| return { task: "detect", classes: det }; | |
| } | |
| } | |
| for (const kw of CLASSIFY_KW) { | |
| if (text.includes(kw)) return { task: "classify" }; | |
| } | |
| for (const kw of INTERPRET_KW) { | |
| if (text.includes(kw)) return { task: "interpret" }; | |
| } | |
| if (hasImage && !hasInterpretation) return { task: "interpret" }; | |
| if (hasInterpretation) { | |
| const { det } = extractClasses(text); | |
| if (det) return { task: "detect", classes: det }; | |
| } | |
| return { task: "general" }; | |
| } | |
| function extractClasses(text) { | |
| const lower = text.toLowerCase(); | |
| for (const [name, groupKey] of ANATOMY_TO_GROUP) { | |
| if (lower.includes(name) && ROUTING_GROUP_MAP[groupKey]) { | |
| const [det, seg] = ROUTING_GROUP_MAP[groupKey]; | |
| return { det, seg }; | |
| } | |
| } | |
| for (const [keywords, det, seg] of KEYWORD_FALLBACK) { | |
| for (const kw of keywords) { | |
| if (lower.includes(kw)) return { det, seg }; | |
| } | |
| } | |
| if (/\bnt\b/.test(lower) && ROUTING_GROUP_MAP.nt_nasal) { | |
| const [det, seg] = ROUTING_GROUP_MAP.nt_nasal; | |
| return { det, seg }; | |
| } | |
| if (/\b(all|every|everything)\b/.test(lower)) return { det: DEFAULT_DETECT_CLASSES, seg: null }; | |
| return { det: null, seg: null }; | |
| } | |