stream2sentence 0.2.4__tar.gz → 0.2.6__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.
- stream2sentence-0.2.6/PKG-INFO +189 -0
- stream2sentence-0.2.6/README.md +172 -0
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/setup.py +4 -4
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/stream2sentence/stream2sentence.py +100 -19
- stream2sentence-0.2.6/stream2sentence.egg-info/PKG-INFO +189 -0
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/stream2sentence.egg-info/SOURCES.txt +0 -1
- stream2sentence-0.2.6/stream2sentence.egg-info/requires.txt +3 -0
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/tests/test_stream_from_llm.py +48 -48
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/tests/test_stream_from_llm_old_api.py +19 -19
- stream2sentence-0.2.4/PKG-INFO +0 -138
- stream2sentence-0.2.4/README.md +0 -121
- stream2sentence-0.2.4/stream2sentence/stream2sentence - Kopie.py +0 -427
- stream2sentence-0.2.4/stream2sentence.egg-info/PKG-INFO +0 -138
- stream2sentence-0.2.4/stream2sentence.egg-info/requires.txt +0 -3
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/setup.cfg +0 -0
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/stream2sentence/__init__.py +0 -0
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/stream2sentence.egg-info/dependency_links.txt +0 -0
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/stream2sentence.egg-info/top_level.txt +0 -0
- {stream2sentence-0.2.4 → stream2sentence-0.2.6}/tests/test_stream2sentence.py +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: stream2sentence
|
|
3
|
+
Version: 0.2.6
|
|
4
|
+
Summary: Real-time processing and delivery of sentences from a continuous stream of characters or text chunks.
|
|
5
|
+
Home-page: https://github.com/KoljaB/stream2sentence
|
|
6
|
+
Author: Kolja Beigel
|
|
7
|
+
Author-email: kolja.beigel@web.de
|
|
8
|
+
Keywords: realtime,text streaming,stream,sentence,sentence detection,sentence generation,tts,speech synthesis,nltk,text analysis,audio processing,boundary detection,sentence boundary detection
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.6
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Requires-Dist: nltk==3.9.1
|
|
15
|
+
Requires-Dist: emoji==2.14.0
|
|
16
|
+
Requires-Dist: stanza==1.9.2
|
|
17
|
+
|
|
18
|
+
# Real-Time Sentence Detection
|
|
19
|
+
|
|
20
|
+
Real-time processing and delivery of sentences from a continuous stream of characters or text chunks.
|
|
21
|
+
|
|
22
|
+
> **Hint:** *If you're interested in state-of-the-art voice solutions you might also want to <strong>have a look at [Linguflex](https://github.com/KoljaB/Linguflex)</strong>, the original project from which stream2sentence is spun off. It lets you control your environment by speaking and is one of the most capable and sophisticated open-source assistants currently available.*
|
|
23
|
+
|
|
24
|
+
## Table of Contents
|
|
25
|
+
|
|
26
|
+
- [Features](#features)
|
|
27
|
+
- [Installation](#installation)
|
|
28
|
+
- [Usage](#usage)
|
|
29
|
+
- [Configuration](#configuration)
|
|
30
|
+
- [Contributing](#contributing)
|
|
31
|
+
- [License](#license)
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- Generates sentences from a stream of text in real-time.
|
|
36
|
+
- Customizable to finetune/balance speed vs reliability.
|
|
37
|
+
- Option to clean the output by removing links and emojis from the detected sentences.
|
|
38
|
+
- Easy to configure and integrate.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install stream2sentence
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
Pass a generator of characters or text chunks to `generate_sentences()` to get a generator of sentences in return.
|
|
49
|
+
|
|
50
|
+
Here's a basic example:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from stream2sentence import generate_sentences
|
|
54
|
+
|
|
55
|
+
# Dummy generator for demonstration
|
|
56
|
+
def dummy_generator():
|
|
57
|
+
yield "This is a sentence. And here's another! Yet, "
|
|
58
|
+
yield "there's more. This ends now."
|
|
59
|
+
|
|
60
|
+
for sentence in generate_sentences(dummy_generator()):
|
|
61
|
+
print(sentence)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This will output:
|
|
65
|
+
```
|
|
66
|
+
This is a sentence.
|
|
67
|
+
And here's another!
|
|
68
|
+
Yet, there's more.
|
|
69
|
+
This ends now.
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
One main use case of this library is enable fast text to speech synthesis in the context of character feeds generated from large language models: this library enables fastest possible access to a complete sentence or sentence fragment (using the quick_yield_single_sentence_fragment flag) that then can be synthesized in realtime. The usage of this is demonstrated in the test_stream_from_llm.py file in the tests directory.
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
The `generate_sentences()` function offers various parameters to fine-tune its behavior:
|
|
77
|
+
|
|
78
|
+
### Core Parameters
|
|
79
|
+
|
|
80
|
+
- `generator: Iterator[str]`
|
|
81
|
+
- The primary input source, yielding chunks of text to be processed.
|
|
82
|
+
- Can be any iterator that emits text chunks of any size.
|
|
83
|
+
|
|
84
|
+
- `context_size: int = 12`
|
|
85
|
+
- Number of characters considered for sentence boundary detection.
|
|
86
|
+
- Larger values improve accuracy but may increase latency.
|
|
87
|
+
- Default: 12 characters
|
|
88
|
+
|
|
89
|
+
- `context_size_look_overhead: int = 12`
|
|
90
|
+
- Additional characters to examine beyond `context_size` for sentence splitting.
|
|
91
|
+
- Enhances sentence detection accuracy.
|
|
92
|
+
- Default: 12 characters
|
|
93
|
+
|
|
94
|
+
- `minimum_sentence_length: int = 10`
|
|
95
|
+
- Minimum character count for a text chunk to be considered a sentence.
|
|
96
|
+
- Shorter fragments are buffered until this threshold is met.
|
|
97
|
+
- Default: 10 characters
|
|
98
|
+
|
|
99
|
+
- `minimum_first_fragment_length: int = 10`
|
|
100
|
+
- Minimum character count required for the first sentence fragment.
|
|
101
|
+
- Ensures the initial output meets a specified length threshold.
|
|
102
|
+
- Default: 10 characters
|
|
103
|
+
|
|
104
|
+
### Yield Control
|
|
105
|
+
|
|
106
|
+
These parameters control how quickly and frequently the generator yields sentence fragments:
|
|
107
|
+
|
|
108
|
+
- `quick_yield_single_sentence_fragment: bool = False`
|
|
109
|
+
- When True, yields the first fragment of the first sentence as quickly as possible.
|
|
110
|
+
- Useful for getting immediate output in real-time applications like speech synthesis.
|
|
111
|
+
- Default: False
|
|
112
|
+
|
|
113
|
+
- `quick_yield_for_all_sentences: bool = False`
|
|
114
|
+
- When True, yields the first fragment of every sentence as quickly as possible.
|
|
115
|
+
- Extends the quick yield behavior to all sentences, not just the first one.
|
|
116
|
+
- Automatically sets `quick_yield_single_sentence_fragment` to True.
|
|
117
|
+
- Default: False
|
|
118
|
+
|
|
119
|
+
- `quick_yield_every_fragment: bool = False`
|
|
120
|
+
- When True, yields every fragment of every sentence as quickly as possible.
|
|
121
|
+
- Provides the most granular output, yielding fragments as soon as they're detected.
|
|
122
|
+
- Automatically sets both `quick_yield_for_all_sentences` and `quick_yield_single_sentence_fragment` to True.
|
|
123
|
+
- Default: False
|
|
124
|
+
|
|
125
|
+
### Text Cleanup
|
|
126
|
+
|
|
127
|
+
- `cleanup_text_links: bool = False`
|
|
128
|
+
- When True, removes hyperlinks from the output sentences.
|
|
129
|
+
- Default: False
|
|
130
|
+
|
|
131
|
+
- `cleanup_text_emojis: bool = False`
|
|
132
|
+
- When True, removes emoji characters from the output sentences.
|
|
133
|
+
- Default: False
|
|
134
|
+
|
|
135
|
+
### Tokenization
|
|
136
|
+
|
|
137
|
+
- `tokenize_sentences: Callable = None`
|
|
138
|
+
- Custom function for sentence tokenization.
|
|
139
|
+
- If None, uses the default tokenizer specified by `tokenizer`.
|
|
140
|
+
- Default: None
|
|
141
|
+
|
|
142
|
+
- `tokenizer: str = "nltk"`
|
|
143
|
+
- Specifies the tokenizer to use. Options: "nltk" or "stanza"
|
|
144
|
+
- Default: "nltk"
|
|
145
|
+
|
|
146
|
+
- `language: str = "en"`
|
|
147
|
+
- Language setting for the tokenizer.
|
|
148
|
+
- Use "en" for English or "multilingual" for Stanza tokenizer.
|
|
149
|
+
- Default: "en"
|
|
150
|
+
|
|
151
|
+
### Debugging and Fine-tuning
|
|
152
|
+
|
|
153
|
+
- `log_characters: bool = False`
|
|
154
|
+
- When True, logs each processed character to the console.
|
|
155
|
+
- Useful for debugging or monitoring real-time processing.
|
|
156
|
+
- Default: False
|
|
157
|
+
|
|
158
|
+
- `sentence_fragment_delimiters: str = ".?!;:,\n…)]}。-"`
|
|
159
|
+
- Characters considered as potential sentence fragment delimiters.
|
|
160
|
+
- Used for quick yielding of sentence fragments.
|
|
161
|
+
- Default: ".?!;:,\n…)]}。-"
|
|
162
|
+
|
|
163
|
+
- `full_sentence_delimiters: str = ".?!\n…。"`
|
|
164
|
+
- Characters considered as full sentence delimiters.
|
|
165
|
+
- Used for more definitive sentence boundary detection.
|
|
166
|
+
- Default: ".?!\n…。"
|
|
167
|
+
|
|
168
|
+
- `force_first_fragment_after_words: int = 15`
|
|
169
|
+
- Forces the yield of the first sentence fragment after this many words.
|
|
170
|
+
- Ensures timely output even with long opening sentences.
|
|
171
|
+
- Default: 15 words
|
|
172
|
+
|
|
173
|
+
## Contributing
|
|
174
|
+
|
|
175
|
+
Any Contributions you make are welcome and **greatly appreciated**.
|
|
176
|
+
|
|
177
|
+
1. **Fork** the Project.
|
|
178
|
+
2. **Create** your Feature Branch (`git checkout -b feature/AmazingFeature`).
|
|
179
|
+
3. **Commit** your Changes (`git commit -m 'Add some AmazingFeature'`).
|
|
180
|
+
4. **Push** to the Branch (`git push origin feature/AmazingFeature`).
|
|
181
|
+
5. **Open** a Pull Request.
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
This project is licensed under the MIT License. For more details, see the [`LICENSE`](LICENSE) file.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
Project created and maintained by [Kolja Beigel](https://github.com/KoljaB).
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Real-Time Sentence Detection
|
|
2
|
+
|
|
3
|
+
Real-time processing and delivery of sentences from a continuous stream of characters or text chunks.
|
|
4
|
+
|
|
5
|
+
> **Hint:** *If you're interested in state-of-the-art voice solutions you might also want to <strong>have a look at [Linguflex](https://github.com/KoljaB/Linguflex)</strong>, the original project from which stream2sentence is spun off. It lets you control your environment by speaking and is one of the most capable and sophisticated open-source assistants currently available.*
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Features](#features)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Usage](#usage)
|
|
12
|
+
- [Configuration](#configuration)
|
|
13
|
+
- [Contributing](#contributing)
|
|
14
|
+
- [License](#license)
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- Generates sentences from a stream of text in real-time.
|
|
19
|
+
- Customizable to finetune/balance speed vs reliability.
|
|
20
|
+
- Option to clean the output by removing links and emojis from the detected sentences.
|
|
21
|
+
- Easy to configure and integrate.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install stream2sentence
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
Pass a generator of characters or text chunks to `generate_sentences()` to get a generator of sentences in return.
|
|
32
|
+
|
|
33
|
+
Here's a basic example:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from stream2sentence import generate_sentences
|
|
37
|
+
|
|
38
|
+
# Dummy generator for demonstration
|
|
39
|
+
def dummy_generator():
|
|
40
|
+
yield "This is a sentence. And here's another! Yet, "
|
|
41
|
+
yield "there's more. This ends now."
|
|
42
|
+
|
|
43
|
+
for sentence in generate_sentences(dummy_generator()):
|
|
44
|
+
print(sentence)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This will output:
|
|
48
|
+
```
|
|
49
|
+
This is a sentence.
|
|
50
|
+
And here's another!
|
|
51
|
+
Yet, there's more.
|
|
52
|
+
This ends now.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
One main use case of this library is enable fast text to speech synthesis in the context of character feeds generated from large language models: this library enables fastest possible access to a complete sentence or sentence fragment (using the quick_yield_single_sentence_fragment flag) that then can be synthesized in realtime. The usage of this is demonstrated in the test_stream_from_llm.py file in the tests directory.
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
The `generate_sentences()` function offers various parameters to fine-tune its behavior:
|
|
60
|
+
|
|
61
|
+
### Core Parameters
|
|
62
|
+
|
|
63
|
+
- `generator: Iterator[str]`
|
|
64
|
+
- The primary input source, yielding chunks of text to be processed.
|
|
65
|
+
- Can be any iterator that emits text chunks of any size.
|
|
66
|
+
|
|
67
|
+
- `context_size: int = 12`
|
|
68
|
+
- Number of characters considered for sentence boundary detection.
|
|
69
|
+
- Larger values improve accuracy but may increase latency.
|
|
70
|
+
- Default: 12 characters
|
|
71
|
+
|
|
72
|
+
- `context_size_look_overhead: int = 12`
|
|
73
|
+
- Additional characters to examine beyond `context_size` for sentence splitting.
|
|
74
|
+
- Enhances sentence detection accuracy.
|
|
75
|
+
- Default: 12 characters
|
|
76
|
+
|
|
77
|
+
- `minimum_sentence_length: int = 10`
|
|
78
|
+
- Minimum character count for a text chunk to be considered a sentence.
|
|
79
|
+
- Shorter fragments are buffered until this threshold is met.
|
|
80
|
+
- Default: 10 characters
|
|
81
|
+
|
|
82
|
+
- `minimum_first_fragment_length: int = 10`
|
|
83
|
+
- Minimum character count required for the first sentence fragment.
|
|
84
|
+
- Ensures the initial output meets a specified length threshold.
|
|
85
|
+
- Default: 10 characters
|
|
86
|
+
|
|
87
|
+
### Yield Control
|
|
88
|
+
|
|
89
|
+
These parameters control how quickly and frequently the generator yields sentence fragments:
|
|
90
|
+
|
|
91
|
+
- `quick_yield_single_sentence_fragment: bool = False`
|
|
92
|
+
- When True, yields the first fragment of the first sentence as quickly as possible.
|
|
93
|
+
- Useful for getting immediate output in real-time applications like speech synthesis.
|
|
94
|
+
- Default: False
|
|
95
|
+
|
|
96
|
+
- `quick_yield_for_all_sentences: bool = False`
|
|
97
|
+
- When True, yields the first fragment of every sentence as quickly as possible.
|
|
98
|
+
- Extends the quick yield behavior to all sentences, not just the first one.
|
|
99
|
+
- Automatically sets `quick_yield_single_sentence_fragment` to True.
|
|
100
|
+
- Default: False
|
|
101
|
+
|
|
102
|
+
- `quick_yield_every_fragment: bool = False`
|
|
103
|
+
- When True, yields every fragment of every sentence as quickly as possible.
|
|
104
|
+
- Provides the most granular output, yielding fragments as soon as they're detected.
|
|
105
|
+
- Automatically sets both `quick_yield_for_all_sentences` and `quick_yield_single_sentence_fragment` to True.
|
|
106
|
+
- Default: False
|
|
107
|
+
|
|
108
|
+
### Text Cleanup
|
|
109
|
+
|
|
110
|
+
- `cleanup_text_links: bool = False`
|
|
111
|
+
- When True, removes hyperlinks from the output sentences.
|
|
112
|
+
- Default: False
|
|
113
|
+
|
|
114
|
+
- `cleanup_text_emojis: bool = False`
|
|
115
|
+
- When True, removes emoji characters from the output sentences.
|
|
116
|
+
- Default: False
|
|
117
|
+
|
|
118
|
+
### Tokenization
|
|
119
|
+
|
|
120
|
+
- `tokenize_sentences: Callable = None`
|
|
121
|
+
- Custom function for sentence tokenization.
|
|
122
|
+
- If None, uses the default tokenizer specified by `tokenizer`.
|
|
123
|
+
- Default: None
|
|
124
|
+
|
|
125
|
+
- `tokenizer: str = "nltk"`
|
|
126
|
+
- Specifies the tokenizer to use. Options: "nltk" or "stanza"
|
|
127
|
+
- Default: "nltk"
|
|
128
|
+
|
|
129
|
+
- `language: str = "en"`
|
|
130
|
+
- Language setting for the tokenizer.
|
|
131
|
+
- Use "en" for English or "multilingual" for Stanza tokenizer.
|
|
132
|
+
- Default: "en"
|
|
133
|
+
|
|
134
|
+
### Debugging and Fine-tuning
|
|
135
|
+
|
|
136
|
+
- `log_characters: bool = False`
|
|
137
|
+
- When True, logs each processed character to the console.
|
|
138
|
+
- Useful for debugging or monitoring real-time processing.
|
|
139
|
+
- Default: False
|
|
140
|
+
|
|
141
|
+
- `sentence_fragment_delimiters: str = ".?!;:,\n…)]}。-"`
|
|
142
|
+
- Characters considered as potential sentence fragment delimiters.
|
|
143
|
+
- Used for quick yielding of sentence fragments.
|
|
144
|
+
- Default: ".?!;:,\n…)]}。-"
|
|
145
|
+
|
|
146
|
+
- `full_sentence_delimiters: str = ".?!\n…。"`
|
|
147
|
+
- Characters considered as full sentence delimiters.
|
|
148
|
+
- Used for more definitive sentence boundary detection.
|
|
149
|
+
- Default: ".?!\n…。"
|
|
150
|
+
|
|
151
|
+
- `force_first_fragment_after_words: int = 15`
|
|
152
|
+
- Forces the yield of the first sentence fragment after this many words.
|
|
153
|
+
- Ensures timely output even with long opening sentences.
|
|
154
|
+
- Default: 15 words
|
|
155
|
+
|
|
156
|
+
## Contributing
|
|
157
|
+
|
|
158
|
+
Any Contributions you make are welcome and **greatly appreciated**.
|
|
159
|
+
|
|
160
|
+
1. **Fork** the Project.
|
|
161
|
+
2. **Create** your Feature Branch (`git checkout -b feature/AmazingFeature`).
|
|
162
|
+
3. **Commit** your Changes (`git commit -m 'Add some AmazingFeature'`).
|
|
163
|
+
4. **Push** to the Branch (`git push origin feature/AmazingFeature`).
|
|
164
|
+
5. **Open** a Pull Request.
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
This project is licensed under the MIT License. For more details, see the [`LICENSE`](LICENSE) file.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
Project created and maintained by [Kolja Beigel](https://github.com/KoljaB).
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
|
|
5
5
|
|
|
6
6
|
setuptools.setup(
|
|
7
7
|
name="stream2sentence",
|
|
8
|
-
version="0.2.
|
|
8
|
+
version="0.2.6",
|
|
9
9
|
author="Kolja Beigel",
|
|
10
10
|
author_email="kolja.beigel@web.de",
|
|
11
11
|
description="Real-time processing and delivery of sentences from a continuous stream of characters or text chunks.",
|
|
@@ -20,9 +20,9 @@ setuptools.setup(
|
|
|
20
20
|
],
|
|
21
21
|
python_requires='>=3.6',
|
|
22
22
|
install_requires=[
|
|
23
|
-
'nltk==3.
|
|
24
|
-
'emoji==2.
|
|
25
|
-
'stanza==1.
|
|
23
|
+
'nltk==3.9.1',
|
|
24
|
+
'emoji==2.14.0',
|
|
25
|
+
'stanza==1.9.2'
|
|
26
26
|
],
|
|
27
27
|
keywords='realtime, text streaming, stream, sentence, sentence detection, sentence generation, tts, speech synthesis, nltk, text analysis, audio processing, boundary detection, sentence boundary detection'
|
|
28
28
|
)
|
|
@@ -175,9 +175,12 @@ def init_tokenizer(tokenizer: str, language: str = "en"):
|
|
|
175
175
|
|
|
176
176
|
def generate_sentences(generator: Iterator[str],
|
|
177
177
|
context_size: int = 12,
|
|
178
|
+
context_size_look_overhead: int = 12,
|
|
178
179
|
minimum_sentence_length: int = 10,
|
|
179
|
-
minimum_first_fragment_length=10,
|
|
180
|
+
minimum_first_fragment_length: int = 10,
|
|
180
181
|
quick_yield_single_sentence_fragment: bool = False,
|
|
182
|
+
quick_yield_for_all_sentences: bool = False,
|
|
183
|
+
quick_yield_every_fragment : bool = False,
|
|
181
184
|
cleanup_text_links: bool = False,
|
|
182
185
|
cleanup_text_emojis: bool = False,
|
|
183
186
|
tokenize_sentences=None,
|
|
@@ -185,7 +188,9 @@ def generate_sentences(generator: Iterator[str],
|
|
|
185
188
|
language: str = "en",
|
|
186
189
|
log_characters: bool = False,
|
|
187
190
|
sentence_fragment_delimiters: str = ".?!;:,\n…)]}。-",
|
|
191
|
+
full_sentence_delimiters: str = ".?!\n…。",
|
|
188
192
|
force_first_fragment_after_words=15,
|
|
193
|
+
debug_mode=False
|
|
189
194
|
) -> Iterator[str]:
|
|
190
195
|
"""
|
|
191
196
|
Generates well-formed sentences from a stream of characters or text chunks
|
|
@@ -198,6 +203,9 @@ def generate_sentences(generator: Iterator[str],
|
|
|
198
203
|
for sentence boundary detection. A larger context improves the
|
|
199
204
|
accuracy of detecting sentence boundaries.
|
|
200
205
|
Default is 12 characters.
|
|
206
|
+
context_size_look_overhead: The number of characters to look
|
|
207
|
+
over the context_size boundaries to detect sentence splitting
|
|
208
|
+
characters (improves sentence detection).
|
|
201
209
|
minimum_sentence_length (int): The minimum number of characters a
|
|
202
210
|
sentence must have. If a sentence is shorter, it will be
|
|
203
211
|
concatenated with the following one, improving the overall
|
|
@@ -208,9 +216,15 @@ def generate_sentences(generator: Iterator[str],
|
|
|
208
216
|
required for the first sentence fragment before yielding.
|
|
209
217
|
Default is 10 characters.
|
|
210
218
|
quick_yield_single_sentence_fragment (bool): If set to True, the
|
|
211
|
-
generator will yield the first sentence fragment as quickly as
|
|
219
|
+
generator will yield the first sentence first fragment as quickly as
|
|
212
220
|
possible. This is particularly useful for real-time applications
|
|
213
221
|
such as speech synthesis.
|
|
222
|
+
quick_yield_for_all_sentences (bool): If set to True, the
|
|
223
|
+
generator will yield every sentence first fragment as quickly as
|
|
224
|
+
possible (not only the first sentence first fragment)
|
|
225
|
+
quick_yield_every_fragment (bool): If set to True, the
|
|
226
|
+
generator not only yield every sentence first fragment, but also every
|
|
227
|
+
following fragment.
|
|
214
228
|
cleanup_text_links (bool): If True, removes hyperlinks from the text
|
|
215
229
|
stream to ensure clean output.
|
|
216
230
|
cleanup_text_emojis (bool): If True, filters out emojis from the text
|
|
@@ -224,10 +238,13 @@ def generate_sentences(generator: Iterator[str],
|
|
|
224
238
|
log_characters (bool): If True, logs each character to the console as
|
|
225
239
|
they are processed.
|
|
226
240
|
sentence_fragment_delimiters (str): A string of characters that are
|
|
227
|
-
considered sentence delimiters. Default is ".?!;:,\n…)]}。-".
|
|
241
|
+
considered sentence fragment delimiters. Default is ".?!;:,\n…)]}。-".
|
|
242
|
+
full_sentence_delimiters (str): A string of characters that are
|
|
243
|
+
considered full sentence delimiters. Default is ".?!\n…。".
|
|
228
244
|
force_first_fragment_after_words (int): The number of words after
|
|
229
245
|
which the first sentence fragment is forced to be yielded.
|
|
230
246
|
Default is 15 words.
|
|
247
|
+
debug_mode (bool): If True, enables debug mode for logging.
|
|
231
248
|
|
|
232
249
|
Yields:
|
|
233
250
|
Iterator[str]: An iterator of complete sentences constructed from the
|
|
@@ -242,24 +259,38 @@ def generate_sentences(generator: Iterator[str],
|
|
|
242
259
|
making it versatile for different types of text processing applications.
|
|
243
260
|
"""
|
|
244
261
|
|
|
262
|
+
# Initialize the tokenizer based on the specified tokenizer and language
|
|
245
263
|
global current_tokenizer
|
|
246
264
|
current_tokenizer = tokenizer
|
|
247
265
|
init_tokenizer(current_tokenizer, language)
|
|
248
266
|
|
|
249
|
-
buffer = ''
|
|
250
|
-
is_first_sentence = True
|
|
267
|
+
buffer = '' # Buffer to hold incoming text for processing
|
|
268
|
+
is_first_sentence = True # Flag to identify the first sentence
|
|
251
269
|
word_count = 0 # Initialize word count
|
|
270
|
+
last_delimiter_position = -1 # Position of last full sentence delimiter
|
|
252
271
|
|
|
272
|
+
# Adjust quick yield flags based on settings
|
|
273
|
+
if quick_yield_every_fragment:
|
|
274
|
+
quick_yield_for_all_sentences = True
|
|
275
|
+
|
|
276
|
+
if quick_yield_for_all_sentences:
|
|
277
|
+
quick_yield_single_sentence_fragment = True
|
|
278
|
+
|
|
279
|
+
# Process each character from the generator
|
|
253
280
|
for char in _generate_characters(generator, log_characters):
|
|
254
281
|
|
|
255
282
|
if char:
|
|
256
283
|
buffer += char
|
|
257
284
|
buffer = buffer.lstrip()
|
|
258
285
|
|
|
259
|
-
# Update word count
|
|
286
|
+
# Update word count on encountering space or sentence fragment delimiter
|
|
260
287
|
if char.isspace() or char in sentence_fragment_delimiters:
|
|
261
288
|
word_count += 1
|
|
262
289
|
|
|
290
|
+
if debug_mode:
|
|
291
|
+
print("\033[36mDebug: Added char, buffer size: \"{}\"\033[0m".format(len(buffer)))
|
|
292
|
+
|
|
293
|
+
# Check conditions to yield first sentence fragment quickly
|
|
263
294
|
if (is_first_sentence
|
|
264
295
|
and len(buffer) > minimum_first_fragment_length
|
|
265
296
|
and quick_yield_single_sentence_fragment):
|
|
@@ -271,30 +302,76 @@ def generate_sentences(generator: Iterator[str],
|
|
|
271
302
|
buffer,
|
|
272
303
|
cleanup_text_links,
|
|
273
304
|
cleanup_text_emojis)
|
|
305
|
+
if debug_mode:
|
|
306
|
+
print("\033[36mDebug: Yielding first sentence fragment: \"{}\"\033[0m".format(yield_text))
|
|
274
307
|
yield yield_text
|
|
275
308
|
buffer = ""
|
|
276
|
-
|
|
309
|
+
if not quick_yield_every_fragment:
|
|
310
|
+
is_first_sentence = False
|
|
277
311
|
continue
|
|
278
312
|
|
|
279
|
-
|
|
313
|
+
# Continue accumulating characters if buffer is under minimum sentence length
|
|
280
314
|
if len(buffer) <= minimum_sentence_length + context_size:
|
|
281
315
|
continue
|
|
282
316
|
|
|
283
|
-
#
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
317
|
+
# Update last delimiter position if a new delimiter is found
|
|
318
|
+
if char in full_sentence_delimiters:
|
|
319
|
+
last_delimiter_position = len(buffer) - 1
|
|
320
|
+
|
|
321
|
+
# Define context window for checking potential sentence boundaries
|
|
322
|
+
context_window_end_pos = len(buffer) - context_size - 1
|
|
323
|
+
context_window_start_pos = context_window_end_pos - context_size_look_overhead
|
|
324
|
+
if context_window_start_pos < 0:
|
|
325
|
+
context_window_start_pos = 0
|
|
326
|
+
|
|
327
|
+
# Tokenize sentences from buffer
|
|
328
|
+
sentences = _tokenize_sentences(buffer, tokenize_sentences)
|
|
329
|
+
|
|
330
|
+
if debug_mode:
|
|
331
|
+
print("\033[36mbuffer: \"{}\"\033[0m".format(buffer))
|
|
332
|
+
print("\033[36mlast_delimiter_position: {}\033[0m".format(last_delimiter_position))
|
|
333
|
+
print("\033[36mlen(sentences) > 2: {}\033[0m".format(len(sentences) > 2))
|
|
334
|
+
print("\033[36mcontext_window_start_pos: {}\033[0m".format(context_window_start_pos))
|
|
335
|
+
print("\033[36mcontext_window_end_pos: {}\033[0m".format(context_window_end_pos))
|
|
336
|
+
|
|
337
|
+
# Process and yield sentences based on conditions
|
|
338
|
+
if (len(sentences) > 2 or
|
|
339
|
+
(last_delimiter_position >= 0 and
|
|
340
|
+
context_window_start_pos <= last_delimiter_position <= context_window_end_pos)
|
|
341
|
+
):
|
|
342
|
+
|
|
290
343
|
if len(sentences) > 1:
|
|
291
|
-
|
|
292
|
-
if
|
|
344
|
+
total_length_except_last = sum(len(sentence) for sentence in sentences[:-1])
|
|
345
|
+
if total_length_except_last >= minimum_sentence_length:
|
|
293
346
|
for sentence in sentences[:-1]:
|
|
294
|
-
|
|
347
|
+
yield_text = _clean_text(
|
|
348
|
+
sentence,
|
|
349
|
+
cleanup_text_links,
|
|
350
|
+
cleanup_text_emojis)
|
|
351
|
+
if debug_mode:
|
|
352
|
+
print("\033[36mDebug: Yielding sentence: \"{}\"\033[0m".format(yield_text))
|
|
353
|
+
yield yield_text
|
|
354
|
+
if quick_yield_for_all_sentences:
|
|
355
|
+
is_first_sentence = True
|
|
356
|
+
|
|
357
|
+
# we need to remember if the buffer ends with space
|
|
358
|
+
# - sentences returned by the tokenizers are rtrimmed
|
|
359
|
+
# - this takes any blank spaces away from the last unfinshed sentence
|
|
360
|
+
# - we have to work around this by re-adding the blank space in this case
|
|
361
|
+
ends_with_space = buffer.endswith(" ")
|
|
362
|
+
|
|
363
|
+
# set buffer to last unfinshed sentence returned by tokenizers
|
|
295
364
|
buffer = sentences[-1]
|
|
296
365
|
|
|
297
|
-
|
|
366
|
+
# reset the blank space if it was there:
|
|
367
|
+
if ends_with_space:
|
|
368
|
+
buffer += " "
|
|
369
|
+
|
|
370
|
+
# reset the last delimiter position after yielding
|
|
371
|
+
last_delimiter_position = -1
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
# Yield remaining buffer as final sentence(s)
|
|
298
375
|
if buffer:
|
|
299
376
|
sentences = _tokenize_sentences(buffer, tokenize_sentences)
|
|
300
377
|
sentence_buffer = ""
|
|
@@ -304,6 +381,8 @@ def generate_sentences(generator: Iterator[str],
|
|
|
304
381
|
if len(sentence_buffer) < minimum_sentence_length:
|
|
305
382
|
sentence_buffer += " "
|
|
306
383
|
continue
|
|
384
|
+
if debug_mode:
|
|
385
|
+
print("\033[36mDebug: Yielding final sentence(s): \"{}\"\033[0m".format(yield_text))
|
|
307
386
|
yield_text = _clean_text(
|
|
308
387
|
sentence_buffer,
|
|
309
388
|
cleanup_text_links,
|
|
@@ -317,4 +396,6 @@ def generate_sentences(generator: Iterator[str],
|
|
|
317
396
|
sentence_buffer,
|
|
318
397
|
cleanup_text_links,
|
|
319
398
|
cleanup_text_emojis)
|
|
399
|
+
if debug_mode:
|
|
400
|
+
print("\033[36mDebug: Yielding remaining text: \"{}\"\033[0m".format(yield_text))
|
|
320
401
|
yield yield_text
|