mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 16:02:24 +00:00
fix(session-search): repair a prose phrase without dropping its stop words
Typo repair re-planned the query from `plan.body`, which prose has already had its stop words removed from. `relay is droppng frames` therefore came back as the plan for `relay dropping frames`, and the phrase rung searched for a sentence nobody wrote: the transcript holds `relay is dropping frames`, so the exact match fell through to AND. The repair now maps over `plan.phrase`, the tokens as typed, and re-plans from those. Only terms the body holds are offered to the corrector, so a stop word is still never repaired, and the re-plan recomputes the body from the corrected sentence exactly as before.
This commit is contained in:
@@ -66,6 +66,18 @@ describe('the route ladder tries phrase, then AND, then repair, then OR', () =>
|
||||
expect(ids(result).sort()).toEqual(['1', '2'])
|
||||
})
|
||||
|
||||
it('keeps the stop words a repaired prose phrase was typed with', async () => {
|
||||
const { db, engine } = await open('ss-engine-typo-phrase')
|
||||
// Two copies, so the repair only suggests a term the index really holds.
|
||||
addSyntheticSession(db, { id: 1, text: 'relay is dropping frames' })
|
||||
addSyntheticSession(db, { id: 2, text: 'dropping frames again here' })
|
||||
// Repairing the body alone would re-plan `relay dropping frames`, which no
|
||||
// phrase in the index can match, and the answer would fall to AND.
|
||||
const result = engine.search({ query: 'relay is droppng frames' })
|
||||
expect(result.planner.route).toBe('typo+phrase')
|
||||
expect(ids(result)).toEqual(['1'])
|
||||
})
|
||||
|
||||
it('keeps every term a repaired literal was typed with', async () => {
|
||||
const { db, engine } = await open('ss-engine-typo-literal')
|
||||
addSyntheticSession(db, { id: 1, text: 'parseJson the data' })
|
||||
|
||||
@@ -180,20 +180,28 @@ export class SessionSearchRetrieval {
|
||||
): SessionSearchQueryPlan | null {
|
||||
const typoRepair = this.typoRepair
|
||||
let changed = false
|
||||
const body = plan.body.map((term) => {
|
||||
// Only the body is a candidate for a correction, but the re-plan is fed the
|
||||
// tokens as typed: re-planning the body alone would hand the phrase rung a
|
||||
// sentence with its stop words already gone, and `relay dropping frames`
|
||||
// cannot match the `relay is dropping frames` that is in the transcript.
|
||||
const repairable = new Set(plan.body.map((term) => term.toLowerCase()))
|
||||
const phrase = plan.phrase.map((token) => {
|
||||
if (!repairable.has(token.toLowerCase())) {
|
||||
return token
|
||||
}
|
||||
// Repaired inside the scope the search will run in, so a spelling only
|
||||
// tool output carries neither suppresses a repair nor becomes one.
|
||||
const fix = typoRepair.correct(term, scope)
|
||||
if (fix && fix !== term.toLowerCase()) {
|
||||
const fix = typoRepair.correct(token, scope)
|
||||
if (fix && fix !== token.toLowerCase()) {
|
||||
changed = true
|
||||
return fix
|
||||
}
|
||||
return term
|
||||
return token
|
||||
})
|
||||
// The repair changes spellings, not the query's character: the re-plan is
|
||||
// told what the original decided so a corrected literal keeps every term it
|
||||
// was typed with.
|
||||
return changed ? planSessionSearchQuery(body.join(' '), plan.literal) : null
|
||||
return changed ? planSessionSearchQuery(phrase.join(' '), plan.literal) : null
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user