diff --git a/web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts index f26d4b8968..c93b53c22c 100644 --- a/web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/correction/distance-modeler.ts @@ -631,9 +631,25 @@ export async function *getBestMatches< if((priorResultsQueue.peek()?.totalCost ?? Number.POSITIVE_INFINITY) <= spaceQueue.peek().currentCost) { const result = priorResultsQueue.dequeue(); - // Just pass it through the filter, even if it _was_ already filtered once before. - filter(result); - return result; + // There's no guarantee that the filter closure is the same instance as + // before. + // + // As a filter function may contain caching and/or deduplication + // components, we pass pre-existing results through the filter so that + // it may reconstruct related state and thus cache/deduplicate new + // results based upon old results. + // + // See `initTokenResultFilterer()`, which maintains a map used for + // deduplication. + // + // As these _are_ pre-existing results, we know that they previously + // passed through the filter with a `true` response. However, as #14366 + // isn't implemented, it IS technically possible that a lower-cost + // result was found after a higher-cost result in some cases; therefore + // there is a chance such a duplicate may exist. On that basis, + // re-filtering even for prior results is reasonably motivated at this + // time. + return filter(result) ? result : null; } let lowestCostSource = spaceQueue.dequeue(); diff --git a/web/src/engine/predictive-text/worker-thread/src/main/correction/legacy-quotient-spur.ts b/web/src/engine/predictive-text/worker-thread/src/main/correction/legacy-quotient-spur.ts index 05cfd7c9d6..cd5fdd28e1 100644 --- a/web/src/engine/predictive-text/worker-thread/src/main/correction/legacy-quotient-spur.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/correction/legacy-quotient-spur.ts @@ -52,7 +52,7 @@ export class LegacyQuotientSpur extends SearchQuotientSpur { return new LegacyQuotientSpur(parentNode, inputs, inputSource) as this; } - protected buildEdgesFromResults(priorResults: ReadonlyArray) { + protected buildEdgesFromResults(priorResults: ReadonlyArray): SearchNode[] { // With a newly-available input, we can extend new input-dependent paths from // our previously-reached 'extractedResults' nodes. let outboundNodes = priorResults.map((result) => { diff --git a/web/src/engine/predictive-text/worker-thread/src/main/predict-helpers.ts b/web/src/engine/predictive-text/worker-thread/src/main/predict-helpers.ts index 773f8a86b2..c16022c227 100644 --- a/web/src/engine/predictive-text/worker-thread/src/main/predict-helpers.ts +++ b/web/src/engine/predictive-text/worker-thread/src/main/predict-helpers.ts @@ -11,9 +11,8 @@ import { ContextState, determineContextSlideTransform } from './correction/conte import { ContextTransition } from './correction/context-transition.js'; import { ExecutionTimer } from './correction/execution-timer.js'; import ModelCompositor from './model-compositor.js'; -import { getBestMatches, SearchNode } from './correction/distance-modeler.js'; -import { SearchQuotientNode } from './correction/search-quotient-node.js'; -import { initTokenResultFilterer, TokenResultMapping } from './correction/token-result-mapping.js'; +import { getBestTokenMatches } from './correction/distance-modeler.js'; +import { TokenResultMapping } from './correction/token-result-mapping.js'; const searchForProperty = defaultWordbreaker.searchForProperty; @@ -535,7 +534,7 @@ export async function correctAndEnumerate( let rawPredictions: CorrectionPredictionTuple[] = []; let bestCorrectionCost: number; const correctionPredictionMap: Record> = {}; - for await(const match of getBestMatches(searchModules, timer, initTokenResultFilterer())) { + for await(const match of getBestTokenMatches(searchModules, timer)) { // Corrections obtained: now to predict from them! const tokenization = tokenizations.find(t => t.spaceId == match.spaceId);