jamspell_layer.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. # -*- coding: utf-8 -*-
  2. """
  3. JamSpell-Korrekturschicht.
  4. Neuronaler kontextueller Spellchecker (~0.2ms/Wort).
  5. """
  6. from pathlib import Path
  7. from trixy_core.stt.layers.base import CorrectionLayer
  8. from trixy_core.utils.debug import pdebug, pwarn
  9. try:
  10. import jamspell
  11. _HAS_JAMSPELL = True
  12. except ImportError:
  13. _HAS_JAMSPELL = False
  14. class JamSpellLayer(CorrectionLayer):
  15. """JamSpell neuronale kontextuelle Korrektur."""
  16. NAME = "jamspell"
  17. def __init__(self) -> None:
  18. self._corrector: "jamspell.TSpellCorrector | None" = None
  19. self._protected_words: set[str] = set()
  20. # Platzhalter fuer geschuetzte Woerter waehrend Korrektur
  21. self._placeholder_map: dict[str, str] = {}
  22. def is_available(self) -> bool:
  23. """Prueft ob jamspell installiert ist."""
  24. return _HAS_JAMSPELL
  25. def initialize(self, config: dict, language: str, protected_words: list[str]) -> bool:
  26. """Laedt vortrainiertes JamSpell-Modell."""
  27. if not _HAS_JAMSPELL:
  28. return False
  29. model_path = config.get("jamspell_model_path", "")
  30. if not model_path or not Path(model_path).exists():
  31. pdebug(f"[JamSpell] Modell nicht gefunden: {model_path}")
  32. return False
  33. try:
  34. self._corrector = jamspell.TSpellCorrector()
  35. if not self._corrector.LoadLangModel(str(model_path)):
  36. pwarn(f"[JamSpell] Modell konnte nicht geladen werden: {model_path}")
  37. self._corrector = None
  38. return False
  39. self._protected_words = {w.lower() for w in protected_words}
  40. # Platzhalter generieren (nicht im Woerterbuch vorkommend)
  41. for i, word in enumerate(protected_words):
  42. self._placeholder_map[word.lower()] = f"XPROTX{i:03d}X"
  43. pdebug(f"[JamSpell] Modell geladen: {model_path}")
  44. return True
  45. except Exception as e:
  46. pwarn(f"[JamSpell] Initialisierung fehlgeschlagen: {e}")
  47. self._corrector = None
  48. return False
  49. def correct(self, text: str) -> str:
  50. """Korrigiert Text mit JamSpell (kontextbasiert)."""
  51. if not self._corrector or not text.strip():
  52. return text
  53. try:
  54. # Geschuetzte Woerter durch Platzhalter ersetzen
  55. processed = text.lower()
  56. used_placeholders: dict[str, str] = {}
  57. for word, placeholder in self._placeholder_map.items():
  58. if word in processed.split():
  59. processed = " ".join(
  60. placeholder if w == word else w
  61. for w in processed.split()
  62. )
  63. used_placeholders[placeholder] = word
  64. # JamSpell korrigieren
  65. corrected = self._corrector.FixFragment(processed)
  66. # Platzhalter zurueckersetzen
  67. for placeholder, original in used_placeholders.items():
  68. corrected = corrected.replace(placeholder.lower(), original)
  69. corrected = corrected.replace(placeholder, original)
  70. if corrected != text.lower():
  71. pdebug(f"[JamSpell] '{text}' → '{corrected}'")
  72. return corrected
  73. return text
  74. except Exception as e:
  75. pdebug(f"[JamSpell] Fehler bei Korrektur: {e}")
  76. return text
  77. def shutdown(self) -> None:
  78. """Gibt Ressourcen frei."""
  79. self._corrector = None