fix(voice): say the quadrant is missing instead of "could not be read"
"ترمیم برای دندون دو" set the treatment type but reported the tooth as
unreadable. Nothing was misheard: position 2 arrived intact, with no
quadrant, because none was spoken — four teeth carry position 2 and the
resolver correctly refused to pick one. Only the label was wrong, and it
sent the clinician looking for a transcription fault.
Adds a tooth_missing_quadrant reason that names what is missing and shows
how to say it ("دو بالا راست"), and tells the model explicitly to report a
quadrant-less number with arch and side null rather than guessing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -150,6 +150,57 @@ describe('resolveToothIntents', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('says the quadrant is missing rather than blaming the words', () => {
|
||||
// Regression: "ترمیم برای دندون دو" reported "could not be read", sending the
|
||||
// clinician to look for a transcription fault. Position 2 was understood fine —
|
||||
// what is missing is the quadrant, and four teeth carry position 2.
|
||||
const bare = {
|
||||
kind: 'positional',
|
||||
arch: null,
|
||||
side: null,
|
||||
position: 2,
|
||||
spoken: 'دندون دو',
|
||||
} as unknown as ToothIntent;
|
||||
|
||||
const result = resolveToothIntents([bare]);
|
||||
expect(result.teeth).toEqual([]);
|
||||
expect(result.unresolved).toEqual([
|
||||
{ spoken: 'دندون دو', reason: 'tooth_missing_quadrant' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('reports a missing quadrant for a half-specified tooth too', () => {
|
||||
// "دو بالا" narrows it to 12 or 22 — still not one tooth, and still not our guess.
|
||||
for (const half of [
|
||||
{ arch: 'upper', side: null },
|
||||
{ arch: null, side: 'patient_right' },
|
||||
]) {
|
||||
const result = resolveToothIntents([
|
||||
{
|
||||
kind: 'positional',
|
||||
...half,
|
||||
position: 2,
|
||||
spoken: 'دو',
|
||||
} as unknown as ToothIntent,
|
||||
]);
|
||||
expect(result.unresolved[0].reason).toBe('tooth_missing_quadrant');
|
||||
}
|
||||
});
|
||||
|
||||
it('still calls an out-of-range position out of range when the quadrant is missing', () => {
|
||||
// Position wins: "nine" is wrong however completely it was said.
|
||||
const result = resolveToothIntents([
|
||||
{
|
||||
kind: 'positional',
|
||||
arch: null,
|
||||
side: null,
|
||||
position: 9,
|
||||
spoken: 'نه',
|
||||
} as unknown as ToothIntent,
|
||||
]);
|
||||
expect(result.unresolved[0].reason).toBe('position_out_of_range');
|
||||
});
|
||||
|
||||
it('keeps unresolved items separate when the model omits the spoken span', () => {
|
||||
// Without `spoken` these are indistinguishable; collapsing them would hide a lost tooth.
|
||||
const result = resolveToothIntents([
|
||||
|
||||
Reference in New Issue
Block a user