hanlp-haystack 2.0.0__tar.gz → 2.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hanlp_haystack-2.1.1/CHANGELOG.md +51 -0
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/LICENSE.txt +1 -1
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/PKG-INFO +3 -1
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/pyproject.toml +5 -1
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/src/haystack_integrations/components/preprocessors/hanlp/__init__.py +1 -0
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/src/haystack_integrations/components/preprocessors/hanlp/chinese_document_splitter.py +24 -10
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/tests/test_chinese_document_splitter.py +0 -9
- hanlp_haystack-2.0.0/CHANGELOG.md +0 -20
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/.gitignore +0 -0
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/README.md +0 -0
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/pydoc/config_docusaurus.yml +0 -0
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/src/haystack_integrations/components/preprocessors/py.typed +0 -0
- {hanlp_haystack-2.0.0 → hanlp_haystack-2.1.1}/tests/__init__.py +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [integrations/hanlp-v2.1.0] - 2026-01-14
|
|
4
|
+
|
|
5
|
+
### 🚀 Features
|
|
6
|
+
|
|
7
|
+
- Update hanlp components to auto call run `warm_up` (#2749)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## [integrations/hanlp-v2.0.0] - 2026-01-12
|
|
11
|
+
|
|
12
|
+
### 🐛 Bug Fixes
|
|
13
|
+
|
|
14
|
+
- Docs: fix hanlp markdown filenames (#2480)
|
|
15
|
+
|
|
16
|
+
### 📚 Documentation
|
|
17
|
+
|
|
18
|
+
- Add pydoc configurations for Docusaurus (#2411)
|
|
19
|
+
|
|
20
|
+
### ⚙️ CI
|
|
21
|
+
|
|
22
|
+
- Change pytest command (#2475)
|
|
23
|
+
|
|
24
|
+
### 🧹 Chores
|
|
25
|
+
|
|
26
|
+
- Remove Readme API CI workflow and configs (#2573)
|
|
27
|
+
- Make fmt command more forgiving (#2671)
|
|
28
|
+
- [**breaking**] Hanlp - drop Python 3.9 and use X|Y typing (#2707)
|
|
29
|
+
|
|
30
|
+
### 🌀 Miscellaneous
|
|
31
|
+
|
|
32
|
+
- Enhancement: Adopt PEP 585 type hinting (part 4) (#2527)
|
|
33
|
+
|
|
34
|
+
## [integrations/hanlp-v1.0.1] - 2025-10-14
|
|
35
|
+
|
|
36
|
+
### 🐛 Bug Fixes
|
|
37
|
+
|
|
38
|
+
- Missing decorator output for ChineseDocumentSplitter (#2377)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### 🧹 Chores
|
|
42
|
+
|
|
43
|
+
- Standardize readmes - part 2 (#2205)
|
|
44
|
+
|
|
45
|
+
## [integrations/hanlp-v1.0.0] - 2025-07-22
|
|
46
|
+
|
|
47
|
+
### 🚀 Features
|
|
48
|
+
|
|
49
|
+
- Add new HanLP integration with ChineseDocumentSplitter (#1943)
|
|
50
|
+
|
|
51
|
+
<!-- generated by git-cliff -->
|
|
@@ -58,7 +58,7 @@ APPENDIX: How to apply the Apache License to your work.
|
|
|
58
58
|
|
|
59
59
|
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
|
|
60
60
|
|
|
61
|
-
Copyright
|
|
61
|
+
Copyright 2025 deepset GmbH
|
|
62
62
|
|
|
63
63
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
64
64
|
you may not use this file except in compliance with the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hanlp-haystack
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: An integration of Han Language Processing - HanLP as a ChineseDocumentSplitter component.
|
|
5
5
|
Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/hanlp#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
|
|
@@ -20,6 +20,8 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
20
20
|
Requires-Python: >=3.10
|
|
21
21
|
Requires-Dist: hanlp>=2.1.1
|
|
22
22
|
Requires-Dist: haystack-ai>=2.22.0
|
|
23
|
+
Requires-Dist: tokenizers>=0.20.2
|
|
24
|
+
Requires-Dist: transformers<5,>=4.1.1
|
|
23
25
|
Description-Content-Type: text/markdown
|
|
24
26
|
|
|
25
27
|
# hanlp-haystack
|
|
@@ -24,7 +24,11 @@ classifiers = [
|
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
26
|
"haystack-ai>=2.22.0",
|
|
27
|
-
"hanlp>=2.1.1"
|
|
27
|
+
"hanlp>=2.1.1",
|
|
28
|
+
# the following pins can be removed once HanLP supports transformers 5
|
|
29
|
+
"transformers>=4.1.1,<5",
|
|
30
|
+
# tokenizers is a dependency of transformers. The following version is the first one which supports Python 3.13.
|
|
31
|
+
"tokenizers>=0.20.2",
|
|
28
32
|
]
|
|
29
33
|
|
|
30
34
|
[project.urls]
|
|
@@ -12,6 +12,7 @@ from haystack.utils import deserialize_callable, serialize_callable
|
|
|
12
12
|
from more_itertools import windowed
|
|
13
13
|
|
|
14
14
|
import hanlp
|
|
15
|
+
from hanlp.common.component import Component as HanlpComponent
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
@@ -47,7 +48,6 @@ class ChineseDocumentSplitter:
|
|
|
47
48
|
splitter = ChineseDocumentSplitter(
|
|
48
49
|
split_by="word", split_length=10, split_overlap=3, respect_sentence_boundary=True
|
|
49
50
|
)
|
|
50
|
-
splitter.warm_up()
|
|
51
51
|
result = splitter.run(documents=[doc])
|
|
52
52
|
print(result["documents"])
|
|
53
53
|
```
|
|
@@ -62,7 +62,7 @@ class ChineseDocumentSplitter:
|
|
|
62
62
|
respect_sentence_boundary: bool = False,
|
|
63
63
|
splitting_function: Callable | None = None,
|
|
64
64
|
granularity: Literal["coarse", "fine"] = "coarse",
|
|
65
|
-
):
|
|
65
|
+
) -> None:
|
|
66
66
|
"""
|
|
67
67
|
Initialize the ChineseDocumentSplitter component.
|
|
68
68
|
|
|
@@ -95,6 +95,9 @@ class ChineseDocumentSplitter:
|
|
|
95
95
|
self.respect_sentence_boundary = respect_sentence_boundary
|
|
96
96
|
self.splitting_function = splitting_function
|
|
97
97
|
self.granularity = granularity
|
|
98
|
+
self.chinese_tokenizer: HanlpComponent | None = None
|
|
99
|
+
self.split_sent: HanlpComponent | None = None
|
|
100
|
+
self._is_warmed_up = False
|
|
98
101
|
|
|
99
102
|
@staticmethod
|
|
100
103
|
def _validate_init_parameters(
|
|
@@ -159,9 +162,8 @@ class ChineseDocumentSplitter:
|
|
|
159
162
|
|
|
160
163
|
:raises RuntimeError: If the Chinese word segmentation model is not loaded.
|
|
161
164
|
"""
|
|
162
|
-
if self.
|
|
163
|
-
|
|
164
|
-
raise RuntimeError(msg)
|
|
165
|
+
if not self._is_warmed_up:
|
|
166
|
+
self.warm_up()
|
|
165
167
|
|
|
166
168
|
split_docs = []
|
|
167
169
|
for doc in documents:
|
|
@@ -170,11 +172,15 @@ class ChineseDocumentSplitter:
|
|
|
170
172
|
|
|
171
173
|
def warm_up(self) -> None:
|
|
172
174
|
"""Warm up the component by loading the necessary models."""
|
|
175
|
+
if self._is_warmed_up:
|
|
176
|
+
return
|
|
177
|
+
|
|
173
178
|
if self.granularity == "coarse":
|
|
174
179
|
self.chinese_tokenizer = hanlp.load(hanlp.pretrained.tok.COARSE_ELECTRA_SMALL_ZH)
|
|
175
180
|
if self.granularity == "fine":
|
|
176
181
|
self.chinese_tokenizer = hanlp.load(hanlp.pretrained.tok.FINE_ELECTRA_SMALL_ZH)
|
|
177
182
|
self.split_sent = hanlp.load(hanlp.pretrained.eos.UD_CTB_EOS_MUL)
|
|
183
|
+
self._is_warmed_up = True
|
|
178
184
|
|
|
179
185
|
def _split_by_character(self, doc: Document) -> list[Document]:
|
|
180
186
|
"""
|
|
@@ -188,7 +194,7 @@ class ChineseDocumentSplitter:
|
|
|
188
194
|
|
|
189
195
|
split_at = _CHARACTER_SPLIT_BY_MAPPING[self.split_by]
|
|
190
196
|
|
|
191
|
-
units = self.chinese_tokenizer(doc.content)
|
|
197
|
+
units = self.chinese_tokenizer(doc.content) # type: ignore[misc]
|
|
192
198
|
|
|
193
199
|
for i in range(len(units) - 1):
|
|
194
200
|
units[i] += split_at
|
|
@@ -211,7 +217,7 @@ class ChineseDocumentSplitter:
|
|
|
211
217
|
:return: A list of split sentences.
|
|
212
218
|
"""
|
|
213
219
|
# Split sentences
|
|
214
|
-
sentences = self.split_sent(text)
|
|
220
|
+
sentences = self.split_sent(text) # type: ignore[misc]
|
|
215
221
|
|
|
216
222
|
# Organize the format of segmented sentences
|
|
217
223
|
results = []
|
|
@@ -261,9 +267,15 @@ class ChineseDocumentSplitter:
|
|
|
261
267
|
|
|
262
268
|
for sentence_idx, sentence in enumerate(sentences):
|
|
263
269
|
current_chunk.append(sentence)
|
|
264
|
-
chunk_word_count += len(
|
|
270
|
+
chunk_word_count += len(
|
|
271
|
+
self.chinese_tokenizer(sentence) # type: ignore[misc]
|
|
272
|
+
)
|
|
265
273
|
next_sentence_word_count = (
|
|
266
|
-
len(
|
|
274
|
+
len(
|
|
275
|
+
self.chinese_tokenizer(sentences[sentence_idx + 1]) # type: ignore[misc]
|
|
276
|
+
)
|
|
277
|
+
if sentence_idx < len(sentences) - 1
|
|
278
|
+
else 0
|
|
267
279
|
)
|
|
268
280
|
|
|
269
281
|
# Number of words in the current chunk plus the next sentence is larger than the split_length,
|
|
@@ -478,7 +490,9 @@ class ChineseDocumentSplitter:
|
|
|
478
490
|
num_words = 0
|
|
479
491
|
|
|
480
492
|
for sent in reversed(sentences[1:]):
|
|
481
|
-
num_words += len(
|
|
493
|
+
num_words += len(
|
|
494
|
+
self.chinese_tokenizer(sent) # type: ignore[misc]
|
|
495
|
+
)
|
|
482
496
|
# If the number of words is larger than the split_length then don't add any more sentences
|
|
483
497
|
if num_words > split_length:
|
|
484
498
|
break
|
|
@@ -93,7 +93,6 @@ class TestChineseDocumentSplitter:
|
|
|
93
93
|
@pytest.mark.integration
|
|
94
94
|
def test_empty_list(self):
|
|
95
95
|
splitter = ChineseDocumentSplitter()
|
|
96
|
-
splitter.warm_up()
|
|
97
96
|
results = splitter.run(documents=[])
|
|
98
97
|
assert results == {"documents": []}
|
|
99
98
|
|
|
@@ -101,7 +100,6 @@ class TestChineseDocumentSplitter:
|
|
|
101
100
|
def test_empty_document(self):
|
|
102
101
|
splitter = ChineseDocumentSplitter()
|
|
103
102
|
documents = [Document(content="")]
|
|
104
|
-
splitter.warm_up()
|
|
105
103
|
results = splitter.run(documents=documents)
|
|
106
104
|
assert results == {"documents": []}
|
|
107
105
|
|
|
@@ -109,7 +107,6 @@ class TestChineseDocumentSplitter:
|
|
|
109
107
|
def test_whitespace_only_document(self):
|
|
110
108
|
splitter = ChineseDocumentSplitter()
|
|
111
109
|
documents = [Document(content=" ")]
|
|
112
|
-
splitter.warm_up()
|
|
113
110
|
results = splitter.run(documents=documents)
|
|
114
111
|
assert len(results["documents"]) == 0
|
|
115
112
|
|
|
@@ -120,7 +117,6 @@ class TestChineseDocumentSplitter:
|
|
|
120
117
|
Document(content="这是另一个测试文本。", meta={"name": "doc 1"}),
|
|
121
118
|
]
|
|
122
119
|
splitter = ChineseDocumentSplitter(split_by="word", split_length=5, split_overlap=2)
|
|
123
|
-
splitter.warm_up()
|
|
124
120
|
result = splitter.run(documents=documents)
|
|
125
121
|
assert len(result["documents"]) == 2
|
|
126
122
|
for doc, split_doc in zip(documents, result["documents"], strict=True):
|
|
@@ -133,7 +129,6 @@ class TestChineseDocumentSplitter:
|
|
|
133
129
|
Document(content="这是第二个测试文本。"),
|
|
134
130
|
]
|
|
135
131
|
splitter = ChineseDocumentSplitter(split_by="word", split_length=5, split_overlap=2)
|
|
136
|
-
splitter.warm_up()
|
|
137
132
|
result = splitter.run(documents=documents)
|
|
138
133
|
assert len(result["documents"]) == 2
|
|
139
134
|
for doc, split_doc in zip(documents, result["documents"], strict=True):
|
|
@@ -142,7 +137,6 @@ class TestChineseDocumentSplitter:
|
|
|
142
137
|
@pytest.mark.integration
|
|
143
138
|
def test_split_by_word(self, sample_text):
|
|
144
139
|
splitter = ChineseDocumentSplitter(split_by="word", granularity="coarse", split_length=5, split_overlap=0)
|
|
145
|
-
splitter.warm_up()
|
|
146
140
|
result = splitter.run(documents=[Document(content=sample_text)])
|
|
147
141
|
docs = result["documents"]
|
|
148
142
|
assert all(isinstance(doc, Document) for doc in docs)
|
|
@@ -153,7 +147,6 @@ class TestChineseDocumentSplitter:
|
|
|
153
147
|
@pytest.mark.integration
|
|
154
148
|
def test_split_by_sentence(self, sample_text):
|
|
155
149
|
splitter = ChineseDocumentSplitter(split_by="sentence", granularity="coarse", split_length=10, split_overlap=0)
|
|
156
|
-
splitter.warm_up()
|
|
157
150
|
result = splitter.run(documents=[Document(content=sample_text)])
|
|
158
151
|
docs = result["documents"]
|
|
159
152
|
assert all(isinstance(doc, Document) for doc in docs), "All docs should be instances of Document"
|
|
@@ -170,7 +163,6 @@ class TestChineseDocumentSplitter:
|
|
|
170
163
|
splitter = ChineseDocumentSplitter(
|
|
171
164
|
split_by="word", split_length=10, split_overlap=3, respect_sentence_boundary=True
|
|
172
165
|
)
|
|
173
|
-
splitter.warm_up()
|
|
174
166
|
result = splitter.run(documents=[doc])
|
|
175
167
|
docs = result["documents"]
|
|
176
168
|
|
|
@@ -193,7 +185,6 @@ class TestChineseDocumentSplitter:
|
|
|
193
185
|
)
|
|
194
186
|
|
|
195
187
|
splitter = ChineseDocumentSplitter(split_by="word", split_length=30, split_overlap=10, granularity="coarse")
|
|
196
|
-
splitter.warm_up()
|
|
197
188
|
result = splitter.run(documents=[doc])
|
|
198
189
|
docs = result["documents"]
|
|
199
190
|
assert len(docs) == 6
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## [integrations/hanlp-v1.0.1] - 2025-10-14
|
|
4
|
-
|
|
5
|
-
### 🐛 Bug Fixes
|
|
6
|
-
|
|
7
|
-
- Missing decorator output for ChineseDocumentSplitter (#2377)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
### 🧹 Chores
|
|
11
|
-
|
|
12
|
-
- Standardize readmes - part 2 (#2205)
|
|
13
|
-
|
|
14
|
-
## [integrations/hanlp-v1.0.0] - 2025-07-22
|
|
15
|
-
|
|
16
|
-
### 🚀 Features
|
|
17
|
-
|
|
18
|
-
- Add new HanLP integration with ChineseDocumentSplitter (#1943)
|
|
19
|
-
|
|
20
|
-
<!-- generated by git-cliff -->
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|