From 37adecdd8c2e4d451f9d960f605afdb2eeaae54a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:15:51 +0000 Subject: [PATCH 01/10] Initial plan From 5389e1ec4155164029d30cce8329fa47a8b5246c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:19:36 +0000 Subject: [PATCH 02/10] Move external dependency imports to class initialization - word_vector/core.py: Move gensim and numpy imports into methods - summarize/keybert.py: Move numpy and transformers imports into methods - summarize/mt5.py: Move transformers imports into __init__ - wangchanberta/core.py: Lazy load tokenizer via helper function - coref/_fastcoref.py: Move spacy import to inside __init__, avoid default mutable arg Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/coref/_fastcoref.py | 10 +++++++--- pythainlp/summarize/keybert.py | 19 +++++++++++-------- pythainlp/summarize/mt5.py | 4 ++-- pythainlp/wangchanberta/core.py | 31 +++++++++++++++++++------------ pythainlp/word_vector/core.py | 12 ++++++------ 5 files changed, 45 insertions(+), 31 deletions(-) diff --git a/pythainlp/coref/_fastcoref.py b/pythainlp/coref/_fastcoref.py index 32c537b3b..2d203aa0a 100644 --- a/pythainlp/coref/_fastcoref.py +++ b/pythainlp/coref/_fastcoref.py @@ -3,14 +3,12 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -import spacy - class FastCoref: def __init__( self, model_name, - nlp=spacy.blank("th"), + nlp=None, device: str = "cpu", type: str = "FCoref", ) -> None: @@ -18,6 +16,12 @@ def __init__( from fastcoref import FCoref as _model else: from fastcoref import LingMessCoref as _model + + if nlp is None: + import spacy + + nlp = spacy.blank("th") + self.model_name = model_name self.nlp = nlp self.model = _model(self.model_name, device=device, nlp=self.nlp) diff --git a/pythainlp/summarize/keybert.py b/pythainlp/summarize/keybert.py index 5b2bfec48..0e3540140 100644 --- a/pythainlp/summarize/keybert.py +++ b/pythainlp/summarize/keybert.py @@ -16,9 +16,6 @@ from collections.abc import Iterable from typing import Optional, Union -import numpy as np -from transformers import pipeline - from pythainlp.corpus import thai_stopwords from pythainlp.tokenize import word_tokenize @@ -27,6 +24,8 @@ class KeyBERT: def __init__( self, model_name: str = "airesearch/wangchanberta-base-att-spm-uncased" ): + from transformers import pipeline + self.ft_pipeline = pipeline( "feature-extraction", tokenizer=model_name, @@ -135,9 +134,11 @@ def extract_keywords( else: return [kw for kw, _ in keywords] - def embed(self, docs: Union[str, list[str]]) -> np.ndarray: + def embed(self, docs: Union[str, list[str]]): """Create an embedding of each input in `docs` by averaging vectors from the last hidden layer. """ + import numpy as np + embs = self.ft_pipeline(docs) if isinstance(docs, str) or len(docs) == 1: # embed doc. return shape = [1, hidden_size] @@ -201,12 +202,14 @@ def _join_ngram(ngrams: list[tuple[str, ...]]) -> list[str]: # type: ignore[typ def _rank_keywords( - doc_vector: np.ndarray, - word_vectors: np.ndarray, + doc_vector, + word_vectors, keywords: list[str], max_keywords: int, ) -> list[tuple[str, float]]: - def l2_norm(v: np.ndarray) -> np.ndarray: + import numpy as np + + def l2_norm(v): vec_size = v.shape[1] result = np.divide( v, @@ -217,7 +220,7 @@ def l2_norm(v: np.ndarray) -> np.ndarray: ) return result - def cosine_sim(a: np.ndarray, b: np.ndarray) -> np.ndarray: + def cosine_sim(a, b): return (np.matmul(a, b.T).T).sum(axis=1) doc_vector = l2_norm(doc_vector) diff --git a/pythainlp/summarize/mt5.py b/pythainlp/summarize/mt5.py index bce375fea..9cf21487b 100644 --- a/pythainlp/summarize/mt5.py +++ b/pythainlp/summarize/mt5.py @@ -6,8 +6,6 @@ from __future__ import annotations -from transformers import MT5ForConditionalGeneration, T5Tokenizer - from pythainlp.summarize import CPE_KMUTT_THAI_SENTENCE_SUM @@ -38,6 +36,8 @@ def __init__( :param str pretrained_mt5_model_name: Name of pretrained model. If empty (default), uses google/mt5-{model_size}. """ + from transformers import MT5ForConditionalGeneration, T5Tokenizer + model_name = "" if not pretrained_mt5_model_name: if model_size not in ["small", "base", "large", "xl", "xxl"]: diff --git a/pythainlp/wangchanberta/core.py b/pythainlp/wangchanberta/core.py index 927cbb755..908d5a49c 100644 --- a/pythainlp/wangchanberta/core.py +++ b/pythainlp/wangchanberta/core.py @@ -7,19 +7,24 @@ import warnings from typing import Union -from transformers import ( - CamembertTokenizer, - pipeline, -) - from pythainlp.tokenize import word_tokenize _model_name = "wangchanberta-base-att-spm-uncased" -_tokenizer = CamembertTokenizer.from_pretrained( - f"airesearch/{_model_name}", revision="main" -) -if _model_name == "wangchanberta-base-att-spm-uncased": - _tokenizer.additional_special_tokens = ["NOTUSED", "NOTUSED", "<_>"] +_tokenizer = None + + +def _get_tokenizer(): + """Get the tokenizer, initializing it if necessary.""" + global _tokenizer + if _tokenizer is None: + from transformers import CamembertTokenizer + + _tokenizer = CamembertTokenizer.from_pretrained( + f"airesearch/{_model_name}", revision="main" + ) + if _model_name == "wangchanberta-base-att-spm-uncased": + _tokenizer.additional_special_tokens = ["NOTUSED", "NOTUSED", "<_>"] + return _tokenizer class ThaiNameTagger: @@ -33,11 +38,13 @@ def __init__(self, dataset_name: str = "thainer", grouped_entities: bool = True) * *thainer* - ThaiNER dataset :param bool grouped_entities: grouped entities """ + from transformers import pipeline + self.dataset_name = dataset_name self.grouped_entities = grouped_entities self.classify_tokens = pipeline( task="ner", - tokenizer=_tokenizer, + tokenizer=_get_tokenizer(), model=f"airesearch/{_model_name}", revision=f"finetuned@{self.dataset_name}-ner", ignore_labels=[], @@ -226,4 +233,4 @@ def segment(text: str) -> list[str]: if not text or not isinstance(text, str): return [] - return _tokenizer.tokenize(text) + return _get_tokenizer().tokenize(text) diff --git a/pythainlp/word_vector/core.py b/pythainlp/word_vector/core.py index 1c4c58e0d..b679a667e 100644 --- a/pythainlp/word_vector/core.py +++ b/pythainlp/word_vector/core.py @@ -3,10 +3,6 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -from gensim.models import KeyedVectors -from gensim.models.keyedvectors import Word2VecKeyedVectors -from numpy import ndarray, zeros - from pythainlp.corpus import get_corpus_path from pythainlp.tokenize import thai2fit_tokenizer, word_tokenize @@ -48,6 +44,8 @@ def load_wordvector(self, model_name: str) -> None: :param str model_name: model name """ + from gensim.models import KeyedVectors + self.model_name = model_name self.model = KeyedVectors.load_word2vec_format( get_corpus_path(self.model_name), @@ -61,7 +59,7 @@ def load_wordvector(self, model_name: str) -> None: else: self.tokenize = word_tokenize - def get_model(self) -> Word2VecKeyedVectors: + def get_model(self): """Get word vector model. :return: `gensim` word2vec model @@ -247,7 +245,7 @@ def similarity(self, word1: str, word2: str) -> float: """ return self.model.similarity(word1, word2) - def sentence_vectorizer(self, text: str, use_mean: bool = True) -> ndarray: + def sentence_vectorizer(self, text: str, use_mean: bool = True): """Converts a Thai sentence into a vector. Specifically, it first tokenizes that text and maps each tokenized word with the word vectors from the model. @@ -290,6 +288,8 @@ def sentence_vectorizer(self, text: str, use_mean: bool = True) -> ndarray: 0.40506999, 1.58591403, 0.63869202, -0.702155 , 1.62977601, 4.52269109, -0.70760502, 0.50952601, -0.914392 , 0.70673105]]) """ + from numpy import zeros + vec = zeros((1, self.WV_DIM)) words = self.tokenize(text) From a8e13e57018c9ffb9979324380df2cfffd62234c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:30:13 +0000 Subject: [PATCH 03/10] Add lazy imports to more modules - augment/lm/wangchanberta.py: Move transformers imports to __init__ - augment/lm/fasttext.py: Move gensim imports to __init__ - coref/han_coref.py: Remove spacy import from default parameter - parse/transformers_ud.py: Move heavy ML imports to __init__ and __call__ Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/augment/lm/fasttext.py | 6 +++--- pythainlp/augment/lm/wangchanberta.py | 10 +++++----- pythainlp/coref/han_coref.py | 4 +--- pythainlp/parse/transformers_ud.py | 25 +++++++++++++------------ 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/pythainlp/augment/lm/fasttext.py b/pythainlp/augment/lm/fasttext.py index 3621a7896..47dfea180 100644 --- a/pythainlp/augment/lm/fasttext.py +++ b/pythainlp/augment/lm/fasttext.py @@ -5,9 +5,6 @@ import itertools -from gensim.models.fasttext import FastText as FastText_gensim -from gensim.models.keyedvectors import KeyedVectors - from pythainlp.tokenize import word_tokenize @@ -20,6 +17,9 @@ class FastTextAug: def __init__(self, model_path: str): """:param str model_path: path of model file """ + from gensim.models.fasttext import FastText as FastText_gensim + from gensim.models.keyedvectors import KeyedVectors + if model_path.endswith(".bin"): self.model = FastText_gensim.load_facebook_vectors(model_path) elif model_path.endswith(".vec"): diff --git a/pythainlp/augment/lm/wangchanberta.py b/pythainlp/augment/lm/wangchanberta.py index 5624e8171..bb4d611a7 100644 --- a/pythainlp/augment/lm/wangchanberta.py +++ b/pythainlp/augment/lm/wangchanberta.py @@ -3,16 +3,16 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -from transformers import ( - CamembertTokenizer, - pipeline, -) - model_name = "airesearch/wangchanberta-base-att-spm-uncased" class Thai2transformersAug: def __init__(self): + from transformers import ( + CamembertTokenizer, + pipeline, + ) + self.model_name = "airesearch/wangchanberta-base-att-spm-uncased" self.target_tokenizer = CamembertTokenizer self.tokenizer = CamembertTokenizer.from_pretrained( diff --git a/pythainlp/coref/han_coref.py b/pythainlp/coref/han_coref.py index 4fd6d2f6e..046c34273 100644 --- a/pythainlp/coref/han_coref.py +++ b/pythainlp/coref/han_coref.py @@ -3,13 +3,11 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -import spacy - from pythainlp.coref._fastcoref import FastCoref class HanCoref(FastCoref): - def __init__(self, device: str = "cpu", nlp=spacy.blank("th")) -> None: + def __init__(self, device: str = "cpu", nlp=None) -> None: super().__init__( model_name="pythainlp/han-coref-v1.0", device=device, nlp=nlp ) diff --git a/pythainlp/parse/transformers_ud.py b/pythainlp/parse/transformers_ud.py index 4a01e8d83..fb5942f3d 100644 --- a/pythainlp/parse/transformers_ud.py +++ b/pythainlp/parse/transformers_ud.py @@ -14,23 +14,20 @@ import os from typing import List, Union -import numpy -import torch -import ufal.chu_liu_edmonds -from transformers import ( - AutoConfig, - AutoModelForQuestionAnswering, - AutoModelForTokenClassification, - AutoTokenizer, - TokenClassificationPipeline, -) -from transformers.utils import cached_file - class Parse: def __init__( self, model: str = "KoichiYasuoka/deberta-base-thai-ud-head" ) -> None: + from transformers import ( + AutoConfig, + AutoModelForQuestionAnswering, + AutoModelForTokenClassification, + AutoTokenizer, + TokenClassificationPipeline, + ) + from transformers.utils import cached_file + if model is None: model = "KoichiYasuoka/deberta-base-thai-ud-head" self.tokenizer = AutoTokenizer.from_pretrained(model) @@ -58,6 +55,10 @@ def __init__( ) def __call__(self, text: str, tag: str = "str") -> Union[List[List[str]], str]: + import numpy + import torch + import ufal.chu_liu_edmonds + w = [ (t["start"], t["end"], t["entity_group"]) for t in self.deprel(text) From b09c344a65617aa98f7a4c7d15814781bf101283 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:35:35 +0000 Subject: [PATCH 04/10] Add TYPE_CHECKING imports and restore type hints - Add TYPE_CHECKING imports for type hints without runtime imports - Restore type hints using proper forward references - Address code review feedback about preserving type information Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/summarize/keybert.py | 18 ++++++++++++------ pythainlp/word_vector/core.py | 10 ++++++++-- tests/data/text-misspelled-r0.05-seed1.txt | 2 +- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/pythainlp/summarize/keybert.py b/pythainlp/summarize/keybert.py index 0e3540140..c701c3ffc 100644 --- a/pythainlp/summarize/keybert.py +++ b/pythainlp/summarize/keybert.py @@ -14,11 +14,14 @@ from collections import Counter from collections.abc import Iterable -from typing import Optional, Union +from typing import TYPE_CHECKING, Optional, Union from pythainlp.corpus import thai_stopwords from pythainlp.tokenize import word_tokenize +if TYPE_CHECKING: + import numpy as np + class KeyBERT: def __init__( @@ -134,8 +137,11 @@ def extract_keywords( else: return [kw for kw, _ in keywords] - def embed(self, docs: Union[str, list[str]]): + def embed(self, docs: Union[str, list[str]]) -> np.ndarray: """Create an embedding of each input in `docs` by averaging vectors from the last hidden layer. + + :return: numpy array of embeddings + :rtype: np.ndarray """ import numpy as np @@ -202,14 +208,14 @@ def _join_ngram(ngrams: list[tuple[str, ...]]) -> list[str]: # type: ignore[typ def _rank_keywords( - doc_vector, - word_vectors, + doc_vector: np.ndarray, + word_vectors: np.ndarray, keywords: list[str], max_keywords: int, ) -> list[tuple[str, float]]: import numpy as np - def l2_norm(v): + def l2_norm(v: np.ndarray) -> np.ndarray: vec_size = v.shape[1] result = np.divide( v, @@ -220,7 +226,7 @@ def l2_norm(v): ) return result - def cosine_sim(a, b): + def cosine_sim(a: np.ndarray, b: np.ndarray) -> np.ndarray: return (np.matmul(a, b.T).T).sum(axis=1) doc_vector = l2_norm(doc_vector) diff --git a/pythainlp/word_vector/core.py b/pythainlp/word_vector/core.py index b679a667e..9abdd5189 100644 --- a/pythainlp/word_vector/core.py +++ b/pythainlp/word_vector/core.py @@ -3,9 +3,15 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations +from typing import TYPE_CHECKING + from pythainlp.corpus import get_corpus_path from pythainlp.tokenize import thai2fit_tokenizer, word_tokenize +if TYPE_CHECKING: + from gensim.models.keyedvectors import Word2VecKeyedVectors + from numpy import ndarray + WV_DIM = 300 # word vector dimension _MODEL_NAME = "thai2fit_wv" @@ -59,7 +65,7 @@ def load_wordvector(self, model_name: str) -> None: else: self.tokenize = word_tokenize - def get_model(self): + def get_model(self) -> Word2VecKeyedVectors: """Get word vector model. :return: `gensim` word2vec model @@ -245,7 +251,7 @@ def similarity(self, word1: str, word2: str) -> float: """ return self.model.similarity(word1, word2) - def sentence_vectorizer(self, text: str, use_mean: bool = True): + def sentence_vectorizer(self, text: str, use_mean: bool = True) -> ndarray: """Converts a Thai sentence into a vector. Specifically, it first tokenizes that text and maps each tokenized word with the word vectors from the model. diff --git a/tests/data/text-misspelled-r0.05-seed1.txt b/tests/data/text-misspelled-r0.05-seed1.txt index f0718da3e..e1098aa72 100644 --- a/tests/data/text-misspelled-r0.05-seed1.txt +++ b/tests/data/text-misspelled-r0.05-seed1.txt @@ -1 +1 @@ -ผมไม่ชอบกินผัก ดังนั้นผมจึงมะกจะเลือปทานอาหารท่่มีเนืัอสัตว์เป็นส่วนใหญ่ อย่างไรก็ตาม ผมก็รู้ว่าการทานผักมีประโยชน์ต่อสุขภาพ ดังนั้นฟมจึงพยายามทานผักบ้างในบางมื้อ ดต่ผมก็ยังคงเลือกทานผักที่ผมชอบเท่านั่น อย่างเช่น ถั่วฝักยาว หรือ ถั่วฝักยาว ฐึ่บผมคิดวรามัรก็เปฺนผักืี่อร่อยและมีประโยชา์ด้วย \ No newline at end of file +ผมไม่ชอบกินผัก ดังนั้นผมจึงมักจะะลือกทานอาหารที่สีเนื้อสัตว์ัป็นส่วนวหญ่ อย่างไรก็ตาม ผมก็รู้ว่าการทานผักมีกระโยชน์ต่อสุขภาพ ดังนั่นผมจึงพยายามทานผักบ้างในบางมื้อ แต่ผมก็ยังคงเลือกทานผักที่ผมชอบัท่านุ้น อย่างเช่น ถั่วฝักย่ว หรือ ถ้่วฝักยาว ซั่งผมคิดใ่ามุนก็เป็นผักที่อร่อยและมีประโยชน์ด้วย \ No newline at end of file From 0282675aaaf2fa791011b01e99c2ed1a1bfeac5d Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Mon, 2 Feb 2026 20:31:04 +0000 Subject: [PATCH 05/10] Update pythainlp/summarize/keybert.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pythainlp/summarize/keybert.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pythainlp/summarize/keybert.py b/pythainlp/summarize/keybert.py index c701c3ffc..2a0dbe998 100644 --- a/pythainlp/summarize/keybert.py +++ b/pythainlp/summarize/keybert.py @@ -138,11 +138,7 @@ def extract_keywords( return [kw for kw, _ in keywords] def embed(self, docs: Union[str, list[str]]) -> np.ndarray: - """Create an embedding of each input in `docs` by averaging vectors from the last hidden layer. - - :return: numpy array of embeddings - :rtype: np.ndarray - """ + """Create an embedding of each input in `docs` by averaging vectors from the last hidden layer.""" import numpy as np embs = self.ft_pipeline(docs) From 4684be159ec56a2d895b6ec0dcd8301c9eb78233 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Mon, 2 Feb 2026 21:11:15 +0000 Subject: [PATCH 06/10] Delete tests/data/text-misspelled-r0.05-seed1.txt --- tests/data/text-misspelled-r0.05-seed1.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tests/data/text-misspelled-r0.05-seed1.txt diff --git a/tests/data/text-misspelled-r0.05-seed1.txt b/tests/data/text-misspelled-r0.05-seed1.txt deleted file mode 100644 index e1098aa72..000000000 --- a/tests/data/text-misspelled-r0.05-seed1.txt +++ /dev/null @@ -1 +0,0 @@ -ผมไม่ชอบกินผัก ดังนั้นผมจึงมักจะะลือกทานอาหารที่สีเนื้อสัตว์ัป็นส่วนวหญ่ อย่างไรก็ตาม ผมก็รู้ว่าการทานผักมีกระโยชน์ต่อสุขภาพ ดังนั่นผมจึงพยายามทานผักบ้างในบางมื้อ แต่ผมก็ยังคงเลือกทานผักที่ผมชอบัท่านุ้น อย่างเช่น ถั่วฝักย่ว หรือ ถ้่วฝักยาว ซั่งผมคิดใ่ามุนก็เป็นผักที่อร่อยและมีประโยชน์ด้วย \ No newline at end of file From 97f4e714c36e30a74d92d5882a17e73f19b3f8c4 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Mon, 2 Feb 2026 21:16:53 +0000 Subject: [PATCH 07/10] Update pythainlp/parse/transformers_ud.py --- pythainlp/parse/transformers_ud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythainlp/parse/transformers_ud.py b/pythainlp/parse/transformers_ud.py index fb5942f3d..740a77440 100644 --- a/pythainlp/parse/transformers_ud.py +++ b/pythainlp/parse/transformers_ud.py @@ -54,7 +54,7 @@ def __init__( model=t, tokenizer=self.tokenizer ) - def __call__(self, text: str, tag: str = "str") -> Union[List[List[str]], str]: + def __call__(self, text: str, tag: str = "str") -> Union[list[list[str]], str]: import numpy import torch import ufal.chu_liu_edmonds From 6a2a3984ff67ddba769610567cc68cd738b6ab0d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 05:38:38 +0000 Subject: [PATCH 08/10] Address review comments: restore test file and use native union syntax - Restore tests/data/text-misspelled-r0.05-seed1.txt (was accidentally deleted) - Use Python 3.9+ native union syntax (list[list[str]] | str) instead of Union - Remove unused typing imports (List, Union) Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/parse/transformers_ud.py | 3 +-- tests/data/text-misspelled-r0.05-seed1.txt | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 tests/data/text-misspelled-r0.05-seed1.txt diff --git a/pythainlp/parse/transformers_ud.py b/pythainlp/parse/transformers_ud.py index 740a77440..ad18673b4 100644 --- a/pythainlp/parse/transformers_ud.py +++ b/pythainlp/parse/transformers_ud.py @@ -12,7 +12,6 @@ from __future__ import annotations import os -from typing import List, Union class Parse: @@ -54,7 +53,7 @@ def __init__( model=t, tokenizer=self.tokenizer ) - def __call__(self, text: str, tag: str = "str") -> Union[list[list[str]], str]: + def __call__(self, text: str, tag: str = "str") -> list[list[str]] | str: import numpy import torch import ufal.chu_liu_edmonds diff --git a/tests/data/text-misspelled-r0.05-seed1.txt b/tests/data/text-misspelled-r0.05-seed1.txt new file mode 100644 index 000000000..e1098aa72 --- /dev/null +++ b/tests/data/text-misspelled-r0.05-seed1.txt @@ -0,0 +1 @@ +ผมไม่ชอบกินผัก ดังนั้นผมจึงมักจะะลือกทานอาหารที่สีเนื้อสัตว์ัป็นส่วนวหญ่ อย่างไรก็ตาม ผมก็รู้ว่าการทานผักมีกระโยชน์ต่อสุขภาพ ดังนั่นผมจึงพยายามทานผักบ้างในบางมื้อ แต่ผมก็ยังคงเลือกทานผักที่ผมชอบัท่านุ้น อย่างเช่น ถั่วฝักย่ว หรือ ถ้่วฝักยาว ซั่งผมคิดใ่ามุนก็เป็นผักที่อร่อยและมีประโยชน์ด้วย \ No newline at end of file From 0ec00ae0b76c2433543618d63934eb7a989c2786 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 06:06:06 +0000 Subject: [PATCH 09/10] Use Union with native list types for Python 3.9 compatibility - Change from PEP 604 union syntax (list[list[str]] | str) to Union[list[list[str]], str] - Maintain native lowercase list types for Python 3.9+ - Ensure compatibility with type inspection tools in Python 3.9 strict mode Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/parse/transformers_ud.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pythainlp/parse/transformers_ud.py b/pythainlp/parse/transformers_ud.py index ad18673b4..b3f432106 100644 --- a/pythainlp/parse/transformers_ud.py +++ b/pythainlp/parse/transformers_ud.py @@ -12,6 +12,7 @@ from __future__ import annotations import os +from typing import Union class Parse: @@ -53,7 +54,7 @@ def __init__( model=t, tokenizer=self.tokenizer ) - def __call__(self, text: str, tag: str = "str") -> list[list[str]] | str: + def __call__(self, text: str, tag: str = "str") -> Union[list[list[str]], str]: import numpy import torch import ufal.chu_liu_edmonds From 9c7b341480577ebe9445a8750ccd8e80b7de58ab Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Tue, 3 Feb 2026 06:51:37 +0000 Subject: [PATCH 10/10] Delete tests/data/text-misspelled-r0.05-seed1.txt --- tests/data/text-misspelled-r0.05-seed1.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tests/data/text-misspelled-r0.05-seed1.txt diff --git a/tests/data/text-misspelled-r0.05-seed1.txt b/tests/data/text-misspelled-r0.05-seed1.txt deleted file mode 100644 index e1098aa72..000000000 --- a/tests/data/text-misspelled-r0.05-seed1.txt +++ /dev/null @@ -1 +0,0 @@ -ผมไม่ชอบกินผัก ดังนั้นผมจึงมักจะะลือกทานอาหารที่สีเนื้อสัตว์ัป็นส่วนวหญ่ อย่างไรก็ตาม ผมก็รู้ว่าการทานผักมีกระโยชน์ต่อสุขภาพ ดังนั่นผมจึงพยายามทานผักบ้างในบางมื้อ แต่ผมก็ยังคงเลือกทานผักที่ผมชอบัท่านุ้น อย่างเช่น ถั่วฝักย่ว หรือ ถ้่วฝักยาว ซั่งผมคิดใ่ามุนก็เป็นผักที่อร่อยและมีประโยชน์ด้วย \ No newline at end of file