everalgo-knowledge 0.2.0__tar.gz → 0.2.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.
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/CHANGELOG.md +16 -1
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/PKG-INFO +2 -3
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/pyproject.toml +2 -3
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_block_split.py +8 -20
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_block_split.py +21 -19
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/.gitignore +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/LICENSE +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/README.md +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/examples/_viz.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/examples/run_one_doc.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/__init__.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_batch_merge.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_classify.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_doc_root.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_flatten.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_llm_json.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_postprocess.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_topic_build.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/extractor.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/__init__.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/en/__init__.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/en/category_classify.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/en/content_merge.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/en/topic_merge.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/en/topic_split.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/en/topic_tree_extract.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/en/uncovered_assign.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/py.typed +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/__init__.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/_loader.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/conftest.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/fixtures/document_confluence.json +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/fixtures/document_confluence.queries.json +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/fixtures/idx_multi_topic.json +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/fixtures/idx_multi_topic.queries.json +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/outputs/.gitignore +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/test_classify_smoke.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/test_extraction_smoke.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/__init__.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_batch_merge.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_classify.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_extractor_e2e.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_flatten_doc_root.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_knowledge_public_api.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_llm_json.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_postprocess.py +0 -0
- {everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_topic_build.py +0 -0
|
@@ -6,6 +6,20 @@ follows [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.1] - 2026-09-16
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- The minimum `everalgo-core` version is now `0.5.1`, because knowledge splitting relies on that release's special-token-safe `count_tokens` behavior.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `split_content_to_blocks` and `split_and_batch_content` no longer raise `ValueError` on a document that quotes an `o200k_base` special-token literal such as `<|endoftext|>` — tokenizer documentation and bug reports could not be ingested at all. `_block_split` had grown a second, independent tokenizer; it now counts through `everalgo._tokenize.count_tokens`, which is the shared entry point fixed in `everalgo-core` (see that changelog for the rationale). Block boundaries and batch budgets for documents without such a literal are unchanged.
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
|
|
21
|
+
- The direct `tiktoken` dependency. The package no longer imports it; token counting goes through `everalgo-core`, which declares it. No action is needed from callers — `tiktoken` still arrives transitively.
|
|
22
|
+
|
|
9
23
|
## [0.2.0] - 2026-09-15
|
|
10
24
|
|
|
11
25
|
### Fixed
|
|
@@ -31,4 +45,5 @@ Previously a reserved namespace with `Private :: Do Not Upload`.
|
|
|
31
45
|
[0.2.0]: https://github.com/EverMind-AI/EverAlgo/releases/tag/everalgo-knowledge/v0.2.0
|
|
32
46
|
[0.2.0rc1]: https://github.com/EverMind-AI/EverAlgo/releases/tag/everalgo-knowledge/v0.2.0rc1
|
|
33
47
|
[0.1.1]: https://github.com/EverMind-AI/EverAlgo/releases/tag/everalgo-knowledge/v0.1.1
|
|
34
|
-
[Unreleased]: https://github.com/EverMind-AI/EverAlgo/compare/everalgo-knowledge/v0.2.
|
|
48
|
+
[Unreleased]: https://github.com/EverMind-AI/EverAlgo/compare/everalgo-knowledge/v0.2.1...HEAD
|
|
49
|
+
[0.2.1]: https://github.com/EverMind-AI/EverAlgo/compare/everalgo-knowledge/v0.2.0...everalgo-knowledge/v0.2.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: everalgo-knowledge
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: EverAlgo knowledge: file-based knowledge extractors producing KnowledgeMemory.
|
|
5
5
|
Project-URL: Homepage, https://github.com/EverMind-AI/EverAlgo
|
|
6
6
|
Project-URL: Repository, https://github.com/EverMind-AI/EverAlgo
|
|
@@ -19,9 +19,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
19
19
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
20
|
Requires-Python: >=3.12
|
|
21
21
|
Requires-Dist: asgiref>=3.0
|
|
22
|
-
Requires-Dist: everalgo-core<2.0.0,>=0.
|
|
22
|
+
Requires-Dist: everalgo-core<2.0.0,>=0.5.1
|
|
23
23
|
Requires-Dist: everalgo-parser<2.0.0,>=0.2.0
|
|
24
|
-
Requires-Dist: tiktoken>=0.7
|
|
25
24
|
Description-Content-Type: text/markdown
|
|
26
25
|
|
|
27
26
|
# everalgo-knowledge
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "everalgo-knowledge"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "EverAlgo knowledge: file-based knowledge extractors producing KnowledgeMemory."
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -21,9 +21,8 @@ classifiers = [
|
|
|
21
21
|
]
|
|
22
22
|
dependencies = [
|
|
23
23
|
"asgiref>=3.0",
|
|
24
|
-
"everalgo-core>=0.
|
|
24
|
+
"everalgo-core>=0.5.1,<2.0.0",
|
|
25
25
|
"everalgo-parser>=0.2.0,<2.0.0",
|
|
26
|
-
"tiktoken>=0.7",
|
|
27
26
|
]
|
|
28
27
|
|
|
29
28
|
[project.urls]
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_block_split.py
RENAMED
|
@@ -17,9 +17,8 @@ the knowledge extractor pipeline.
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
19
|
import re
|
|
20
|
-
from functools import lru_cache
|
|
21
20
|
|
|
22
|
-
import
|
|
21
|
+
from everalgo._tokenize import count_tokens
|
|
23
22
|
|
|
24
23
|
__all__ = [
|
|
25
24
|
"DEFAULT_MAX_TOKENS_PER_ATOM",
|
|
@@ -37,8 +36,6 @@ __all__ = [
|
|
|
37
36
|
TABLE_START_MARKER = "TABLE_START"
|
|
38
37
|
TABLE_END_MARKER = "TABLE_END"
|
|
39
38
|
|
|
40
|
-
_O200K_ENCODING_NAME = "o200k_base"
|
|
41
|
-
|
|
42
39
|
# Default token budget per LLM-window batch. 80K leaves headroom on a 128K-context
|
|
43
40
|
# model after the topic-extraction prompt + JSON response. Tunable per call.
|
|
44
41
|
DEFAULT_MAX_TOKENS_PER_BATCH = 80_000
|
|
@@ -52,12 +49,6 @@ DEFAULT_MAX_TOKENS_PER_BATCH = 80_000
|
|
|
52
49
|
DEFAULT_MAX_TOKENS_PER_ATOM = DEFAULT_MAX_TOKENS_PER_BATCH // 4
|
|
53
50
|
|
|
54
51
|
|
|
55
|
-
@lru_cache(maxsize=1)
|
|
56
|
-
def _get_tokenizer() -> tiktoken.Encoding:
|
|
57
|
-
"""Return the shared ``o200k_base`` encoding, initialising on first call."""
|
|
58
|
-
return tiktoken.get_encoding(_O200K_ENCODING_NAME)
|
|
59
|
-
|
|
60
|
-
|
|
61
52
|
def preprocess_content(content: str) -> str:
|
|
62
53
|
"""Normalize parsed text before block splitting.
|
|
63
54
|
|
|
@@ -144,8 +135,7 @@ def _split_long_line(text: str, max_tokens: int) -> list[str]:
|
|
|
144
135
|
66,000-character CJK line, which came back 66,006 characters with mojibake at
|
|
145
136
|
every seam. Character slices concatenate back to the original exactly.
|
|
146
137
|
"""
|
|
147
|
-
|
|
148
|
-
tokens_per_char = len(enc.encode(text)) / len(text)
|
|
138
|
+
tokens_per_char = count_tokens(text) / len(text)
|
|
149
139
|
# 0.9 leaves margin for a denser stretch than the whole-line average.
|
|
150
140
|
window = max(1, int(max_tokens / tokens_per_char * 0.9))
|
|
151
141
|
|
|
@@ -153,7 +143,7 @@ def _split_long_line(text: str, max_tokens: int) -> list[str]:
|
|
|
153
143
|
start = 0
|
|
154
144
|
while start < len(text):
|
|
155
145
|
end = min(start + window, len(text))
|
|
156
|
-
while end - start > 1 and
|
|
146
|
+
while end - start > 1 and count_tokens(text[start:end]) > max_tokens:
|
|
157
147
|
end = start + (end - start) * 3 // 4
|
|
158
148
|
pieces.append(text[start:end])
|
|
159
149
|
start = end
|
|
@@ -168,11 +158,10 @@ def _split_oversized_atom(text: str, max_tokens: int) -> list[str]:
|
|
|
168
158
|
The pieces stay adjacent in the atom list, so a topic that claims all of them
|
|
169
159
|
reassembles the original text through ``_topic_build._rebuild_content``.
|
|
170
160
|
"""
|
|
171
|
-
enc = _get_tokenizer()
|
|
172
161
|
lines = text.split("\n")
|
|
173
162
|
header = _table_header(lines)
|
|
174
163
|
header_text = "\n".join(header)
|
|
175
|
-
header_tokens =
|
|
164
|
+
header_tokens = count_tokens(header_text) if header else 0
|
|
176
165
|
|
|
177
166
|
pieces: list[str] = []
|
|
178
167
|
current: list[str] = []
|
|
@@ -182,12 +171,12 @@ def _split_oversized_atom(text: str, max_tokens: int) -> list[str]:
|
|
|
182
171
|
if current:
|
|
183
172
|
pieces.append("\n".join(header + current) if header else "\n".join(current))
|
|
184
173
|
|
|
185
|
-
newline_tokens =
|
|
174
|
+
newline_tokens = count_tokens("\n")
|
|
186
175
|
for line in lines[len(header) :]:
|
|
187
176
|
# Count the "\n" that ``join`` will insert. Omitting it undercounts a piece by
|
|
188
177
|
# roughly one token per line, which pushes long runs over the budget and drops
|
|
189
178
|
# them into the character-boundary fallback — splitting list items mid-item.
|
|
190
|
-
line_tokens =
|
|
179
|
+
line_tokens = count_tokens(line) + newline_tokens
|
|
191
180
|
if current and current_tokens + line_tokens > max_tokens:
|
|
192
181
|
flush()
|
|
193
182
|
current, current_tokens = [line], header_tokens + line_tokens
|
|
@@ -204,7 +193,7 @@ def _split_oversized_atom(text: str, max_tokens: int) -> list[str]:
|
|
|
204
193
|
# PDF often arrives as one very long unwrapped line.
|
|
205
194
|
bounded: list[str] = []
|
|
206
195
|
for piece in pieces:
|
|
207
|
-
if
|
|
196
|
+
if count_tokens(piece) <= max_tokens:
|
|
208
197
|
bounded.append(piece)
|
|
209
198
|
else:
|
|
210
199
|
bounded.extend(_split_long_line(piece, max_tokens))
|
|
@@ -297,13 +286,12 @@ def split_and_batch_content(
|
|
|
297
286
|
if not atoms:
|
|
298
287
|
return []
|
|
299
288
|
|
|
300
|
-
enc = _get_tokenizer()
|
|
301
289
|
batches: list[list[tuple[int, str]]] = []
|
|
302
290
|
current_batch: list[tuple[int, str]] = []
|
|
303
291
|
current_tokens = 0
|
|
304
292
|
|
|
305
293
|
for atom in atoms:
|
|
306
|
-
atom_tokens =
|
|
294
|
+
atom_tokens = count_tokens(atom[1])
|
|
307
295
|
if current_batch and current_tokens + atom_tokens > max_tokens:
|
|
308
296
|
batches.append(current_batch)
|
|
309
297
|
current_batch = [atom]
|
|
@@ -4,12 +4,12 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
+
from everalgo._tokenize import count_tokens
|
|
7
8
|
from everalgo.knowledge._block_split import (
|
|
8
9
|
DEFAULT_MAX_TOKENS_PER_ATOM,
|
|
9
10
|
DEFAULT_MAX_TOKENS_PER_BATCH,
|
|
10
11
|
TABLE_END_MARKER,
|
|
11
12
|
TABLE_START_MARKER,
|
|
12
|
-
_get_tokenizer,
|
|
13
13
|
format_numbered_paragraphs,
|
|
14
14
|
preprocess_content,
|
|
15
15
|
split_and_batch_content,
|
|
@@ -143,13 +143,12 @@ def _table(rows: int, *, header: bool = True) -> str:
|
|
|
143
143
|
|
|
144
144
|
def test_unbounded_table_merge_no_longer_yields_an_over_budget_atom() -> None:
|
|
145
145
|
"""A page that is mostly one table used to collapse into a single mega-atom."""
|
|
146
|
-
enc = _get_tokenizer()
|
|
147
146
|
doc = f"# Title\n\n## Variants\n\n{_table(6000)}\n\n## References\n\nSee the index.\n"
|
|
148
147
|
|
|
149
148
|
atoms = split_content_to_blocks(doc)
|
|
150
149
|
|
|
151
150
|
assert len(atoms) > 6, "the table must no longer be one atom"
|
|
152
|
-
assert all(
|
|
151
|
+
assert all(count_tokens(t) <= DEFAULT_MAX_TOKENS_PER_ATOM for _, t in atoms)
|
|
153
152
|
assert [i for i, _ in atoms] == list(range(len(atoms))), "ids stay dense and sequential"
|
|
154
153
|
|
|
155
154
|
|
|
@@ -173,13 +172,12 @@ def test_oversized_atom_pieces_preserve_every_row() -> None:
|
|
|
173
172
|
|
|
174
173
|
|
|
175
174
|
def test_long_list_is_split_without_a_header() -> None:
|
|
176
|
-
enc = _get_tokenizer()
|
|
177
175
|
doc = "\n".join(f"- Item {i}: the polished lens recipe was introduced here." for i in range(8000))
|
|
178
176
|
|
|
179
177
|
atoms = split_content_to_blocks(doc)
|
|
180
178
|
|
|
181
179
|
assert len(atoms) > 1
|
|
182
|
-
assert all(
|
|
180
|
+
assert all(count_tokens(t) <= DEFAULT_MAX_TOKENS_PER_ATOM for _, t in atoms)
|
|
183
181
|
assert [ln for _, t in atoms for ln in t.splitlines()] == doc.splitlines()
|
|
184
182
|
|
|
185
183
|
|
|
@@ -191,13 +189,12 @@ def test_ordinary_document_is_untouched_by_the_cap() -> None:
|
|
|
191
189
|
|
|
192
190
|
|
|
193
191
|
def test_cap_is_configurable_and_zero_disables_it() -> None:
|
|
194
|
-
enc = _get_tokenizer()
|
|
195
192
|
doc = _table(2000)
|
|
196
193
|
|
|
197
194
|
assert len(split_content_to_blocks(doc, max_atom_tokens=0)) == 1
|
|
198
195
|
tight = split_content_to_blocks(doc, max_atom_tokens=5_000)
|
|
199
196
|
assert len(tight) > 1
|
|
200
|
-
assert all(
|
|
197
|
+
assert all(count_tokens(t) <= 5_000 for _, t in tight)
|
|
201
198
|
|
|
202
199
|
|
|
203
200
|
# ── adversarial: ways an atom could still slip past the cap ──────────
|
|
@@ -216,64 +213,59 @@ def test_cap_is_configurable_and_zero_disables_it() -> None:
|
|
|
216
213
|
)
|
|
217
214
|
def test_cap_holds_for_every_script(label: str, unit: str) -> None:
|
|
218
215
|
"""A character-count pre-filter is unsound: CJK and emoji exceed 1 token/char."""
|
|
219
|
-
enc = _get_tokenizer()
|
|
220
216
|
cap = 4_000
|
|
221
217
|
doc = "\n".join(f"- {unit}" for _ in range(2_000))
|
|
222
218
|
|
|
223
219
|
atoms = split_content_to_blocks(doc, max_atom_tokens=cap)
|
|
224
220
|
|
|
225
|
-
oversized = [(i,
|
|
221
|
+
oversized = [(i, count_tokens(t)) for i, t in atoms if count_tokens(t) > cap]
|
|
226
222
|
assert not oversized, f"{label}: atoms over cap {cap}: {oversized}"
|
|
227
223
|
|
|
228
224
|
|
|
229
225
|
def test_single_line_longer_than_the_cap_is_split_on_token_boundaries() -> None:
|
|
230
226
|
"""CJK from a PDF often arrives as one very long unwrapped line."""
|
|
231
|
-
enc = _get_tokenizer()
|
|
232
227
|
cap = 1_000
|
|
233
228
|
doc = "洗涤剂的总活性物含量决定去污能力和溶解性能。" * 3_000 # one line, no breaks
|
|
234
229
|
|
|
235
230
|
atoms = split_content_to_blocks(doc, max_atom_tokens=cap)
|
|
236
231
|
|
|
237
232
|
assert len(atoms) > 1
|
|
238
|
-
assert all(
|
|
233
|
+
assert all(count_tokens(t) <= cap for _, t in atoms)
|
|
239
234
|
assert "".join(t for _, t in atoms) == doc.strip(), "no content may be lost"
|
|
240
235
|
|
|
241
236
|
|
|
242
237
|
def test_table_marker_path_is_also_capped() -> None:
|
|
243
238
|
"""Rule 1 (TABLE_START/END) is unbounded too, not just the native-row merge."""
|
|
244
|
-
enc = _get_tokenizer()
|
|
245
239
|
cap = 4_000
|
|
246
240
|
rows = "\n".join(f"<tr><td>Variant {i}</td><td>Wandering merchant</td></tr>" for i in range(3_000))
|
|
247
241
|
doc = f"# Title\n\n{TABLE_START_MARKER}\n{rows}\n{TABLE_END_MARKER}\n\nAfter.\n"
|
|
248
242
|
|
|
249
243
|
atoms = split_content_to_blocks(doc, max_atom_tokens=cap)
|
|
250
244
|
|
|
251
|
-
assert all(
|
|
245
|
+
assert all(count_tokens(t) <= cap for _, t in atoms)
|
|
252
246
|
assert atoms[-1][1] == "After."
|
|
253
247
|
|
|
254
248
|
|
|
255
249
|
def test_table_without_a_separator_row_still_splits() -> None:
|
|
256
250
|
"""Header detection must not be a precondition for the cap to apply."""
|
|
257
|
-
enc = _get_tokenizer()
|
|
258
251
|
cap = 4_000
|
|
259
252
|
doc = "\n".join(f"| Variant {i} | Wandering merchant, biome {i % 40} |" for i in range(3_000))
|
|
260
253
|
|
|
261
254
|
atoms = split_content_to_blocks(doc, max_atom_tokens=cap)
|
|
262
255
|
|
|
263
256
|
assert len(atoms) > 1
|
|
264
|
-
assert all(
|
|
257
|
+
assert all(count_tokens(t) <= cap for _, t in atoms)
|
|
265
258
|
|
|
266
259
|
|
|
267
260
|
def test_header_larger_than_the_cap_does_not_loop_or_drop_content() -> None:
|
|
268
261
|
"""Degenerate: the repeated header alone already exceeds the budget."""
|
|
269
|
-
enc = _get_tokenizer()
|
|
270
262
|
header = "| " + " | ".join(f"column_{i}" for i in range(600)) + " |"
|
|
271
263
|
doc = header + "\n|" + "---|" * 600 + "\n" + "\n".join(f"| row {i} |" for i in range(500))
|
|
272
264
|
|
|
273
265
|
atoms = split_content_to_blocks(doc, max_atom_tokens=500)
|
|
274
266
|
|
|
275
267
|
assert atoms, "must not return empty"
|
|
276
|
-
assert all(
|
|
268
|
+
assert all(count_tokens(t) <= 500 for _, t in atoms)
|
|
277
269
|
|
|
278
270
|
|
|
279
271
|
def test_ids_stay_dense_after_splitting_several_atoms() -> None:
|
|
@@ -290,7 +282,6 @@ def test_ids_stay_dense_after_splitting_several_atoms() -> None:
|
|
|
290
282
|
|
|
291
283
|
def test_split_atoms_batch_within_budget_end_to_end() -> None:
|
|
292
284
|
"""The whole point: no batch may exceed the window after splitting."""
|
|
293
|
-
enc = _get_tokenizer()
|
|
294
285
|
rows = "\n".join(
|
|
295
286
|
f"| Shiny variant #{i:04d} | Trading with the wandering merchant in biome {i % 40} "
|
|
296
287
|
f"| {'Rare' if i % 3 else 'Common'} | Introduced in the winter update. |"
|
|
@@ -300,5 +291,16 @@ def test_split_atoms_batch_within_budget_end_to_end() -> None:
|
|
|
300
291
|
|
|
301
292
|
batches = split_and_batch_content(split_content_to_blocks(preprocess_content(doc)))
|
|
302
293
|
|
|
303
|
-
over = [
|
|
294
|
+
over = [count_tokens(format_numbered_paragraphs(b)) for b in batches]
|
|
304
295
|
assert all(n <= DEFAULT_MAX_TOKENS_PER_BATCH for n in over), f"over-budget batches: {over}"
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def test_split_handles_special_token_literals() -> None:
|
|
299
|
+
"""Block splitting counts tokens, so it shared the tokenizer defect.
|
|
300
|
+
|
|
301
|
+
A knowledge document about tokenizers quotes these literals; before the shared encode
|
|
302
|
+
wrapper that document could not be ingested at all.
|
|
303
|
+
"""
|
|
304
|
+
content = "The tokenizer reserves the literal `<|endoftext|>` as a control token.\n\n" * 20
|
|
305
|
+
blocks = split_content_to_blocks(content)
|
|
306
|
+
assert blocks # split, not raised
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_batch_merge.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_postprocess.py
RENAMED
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/_topic_build.py
RENAMED
|
File without changes
|
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/__init__.py
RENAMED
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/src/everalgo/knowledge/prompts/en/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/fixtures/idx_multi_topic.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/test_classify_smoke.py
RENAMED
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/functional/test_extraction_smoke.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_flatten_doc_root.py
RENAMED
|
File without changes
|
{everalgo_knowledge-0.2.0 → everalgo_knowledge-0.2.1}/tests/knowledge/test_knowledge_public_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|