diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..4ae275d --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-10-31 - Memoizing Lexicon Regex Compilations +**Learning:** In the openmed NLP pipelines (e.g., `openmed.clinical.context`), deterministic regex compilations (`_compiled_context_lexicon`) and lexicon generation can cause severe performance bottlenecks during repeated evaluations across text spans. +**Action:** Always memoize deterministic lexicon and regex compilations (using `@functools.lru_cache`) when processing spans or lexicons dynamically in text pipelines. diff --git a/openmed/openmed/clinical/context.py b/openmed/openmed/clinical/context.py index 9fd11df..439abc2 100644 --- a/openmed/openmed/clinical/context.py +++ b/openmed/openmed/clinical/context.py @@ -35,6 +35,7 @@ from __future__ import annotations +import functools import re from collections.abc import Iterable, Iterator, Mapping, Sequence from dataclasses import dataclass, replace @@ -154,6 +155,7 @@ class _CompiledContextLexicon: backward_context_cues: frozenset[str] +@functools.lru_cache(maxsize=16) def _compiled_context_lexicon(language: str | None = None) -> _CompiledContextLexicon: lexicon = get_clinical_cue_lexicon(language) token_boundaries = lexicon.token_boundaries diff --git a/openmed/openmed/clinical/experiencer.py b/openmed/openmed/clinical/experiencer.py index 8373a72..be1b6a8 100644 --- a/openmed/openmed/clinical/experiencer.py +++ b/openmed/openmed/clinical/experiencer.py @@ -22,6 +22,7 @@ from __future__ import annotations +import functools import re from collections.abc import Mapping from dataclasses import dataclass, replace @@ -106,6 +107,7 @@ _CLAUSE_BOUNDARY_RE = re.compile(r"[.!?;]") +@functools.lru_cache(maxsize=512) def _cue_pattern(cues: tuple[str, ...]) -> re.Pattern[str]: alternation = "|".join( r"\s+".join(re.escape(part) for part in cue.split())