bpp-format 0.3.0__tar.gz → 0.4.0__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.
- {bpp_format-0.3.0 → bpp_format-0.4.0}/PKG-INFO +64 -39
- {bpp_format-0.3.0 → bpp_format-0.4.0}/README.md +63 -38
- {bpp_format-0.3.0 → bpp_format-0.4.0}/pyproject.toml +1 -1
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/__init__.py +6 -4
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/cli.py +52 -21
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/decoder.py +122 -70
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/encoder.py +210 -68
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/estimate.py +2 -2
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/lexer.py +22 -11
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/markdown.py +80 -3
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/PKG-INFO +64 -39
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/SOURCES.txt +1 -0
- bpp_format-0.4.0/tests/test_bpp4.py +234 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_cli.py +20 -3
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_js_parity.py +40 -8
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_markdown.py +6 -2
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_syntax.py +7 -7
- {bpp_format-0.3.0 → bpp_format-0.4.0}/LICENSE +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/setup.cfg +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/__main__.py +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/formats.py +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/tokens.py +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/dependency_links.txt +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/entry_points.txt +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/requires.txt +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/top_level.txt +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_bench.py +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_formats.py +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_property.py +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_roundtrip.py +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_site.py +0 -0
- {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_tokens.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bpp-format
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A token-efficient text format for feeding structured data and plans to LLMs, with a lossless JSON/YAML/CSV converter.
|
|
5
5
|
Author: Furkan
|
|
6
6
|
License-Expression: MIT
|
|
@@ -39,6 +39,11 @@ language models read with far fewer tokens. You can convert it back without losi
|
|
|
39
39
|
every example.
|
|
40
40
|
* **Lossless round trip:** `decode(encode(x)) == x` for JSON and YAML, with types preserved.
|
|
41
41
|
CSV cells come back byte for byte. Markdown plans keep their structure.
|
|
42
|
+
* **Same accuracy as JSON in a first comprehension test.** 6 datasets, 10 auto-graded questions
|
|
43
|
+
each, same data in every format. We set the bar before running: within 2 points of JSON counts
|
|
44
|
+
as "same accuracy". gpt-oss-120b: **84.2% from bpp vs 85.8% from pretty JSON** (2 of 120
|
|
45
|
+
questions). qwen3.8-27b: **80.0% from bpp and from minified JSON**. Small open models, small
|
|
46
|
+
samples; the caveats are [below](#honest-caveats).
|
|
42
47
|
* **Measured, not invented.** Every syntax choice was benchmarked on two tokenizers. There are no
|
|
43
48
|
made-up symbols or binary tricks, only patterns models already know from YAML, CSV, JSON and
|
|
44
49
|
TypeScript.
|
|
@@ -52,7 +57,7 @@ token counts, with no install needed.
|
|
|
52
57
|
```
|
|
53
58
|
JSON, pretty-printed (146 tokens) bpp (53 tokens)
|
|
54
59
|
|
|
55
|
-
{
|
|
60
|
+
{ bpp4
|
|
56
61
|
"store": "Downtown Branch", store Downtown Branch
|
|
57
62
|
"orders": [ orders[3]{id product qty price customer}
|
|
58
63
|
{"id": 1, "customer": "Alice Johnson", 1 Headphones 2 79.9 Alice Johnson
|
|
@@ -69,11 +74,11 @@ Token counts per format for six example files (o200k tokenizer; fewer is better)
|
|
|
69
74
|
|---|---:|---:|---:|---:|---:|---:|---|
|
|
70
75
|
| 60×10 table (CSV) | 5538 | 3562 | 4388 | 2277 | – | **2042** | −6% vs CSV |
|
|
71
76
|
| nested config (YAML) | 601 | 365 | 443 | 399 | – | **323** | −12% vs minified JSON |
|
|
72
|
-
| project plan with deps (JSON) | 1609 | 990 | 1212 | 1228 | – | **
|
|
73
|
-
| Markdown checklist plan |
|
|
77
|
+
| project plan with deps (JSON) | 1609 | 990 | 1212 | 1228 | – | **634** | −36% vs minified JSON |
|
|
78
|
+
| Markdown checklist plan | 1445 | 990 | 1083 | 1024 | 837 | **758** | −9% vs Markdown |
|
|
74
79
|
| API response with nested objects | 3524 | 2231 | 2636 | 2276 | – | **1151** | −48% vs minified JSON |
|
|
75
80
|
| repetitive logs | 5629 | 4109 | 4587 | 3348 | – | **1857** | −42% vs CSV |
|
|
76
|
-
| **total** |
|
|
81
|
+
| **total** | 18346 | 12247 | 14349 | 10552 | | **6765** | **−63% vs JSON, −36% vs TOON** |
|
|
77
82
|
|
|
78
83
|
Anthropic's published Claude tokenizer (`claude2`) shows the same picture: −62% vs JSON, −36% vs
|
|
79
84
|
TOON. The full tables, the method and the cases where bpp wins by less are in
|
|
@@ -86,18 +91,19 @@ TOON. The full tables, the method and the cases where bpp wins by less are in
|
|
|
86
91
|
You need Python 3.9 or newer (check with `python --version`). Then run:
|
|
87
92
|
|
|
88
93
|
```bash
|
|
89
|
-
pip install
|
|
94
|
+
pip install bpp-format
|
|
90
95
|
```
|
|
91
96
|
|
|
92
|
-
|
|
93
|
-
on macOS/Linux or `py -m pip` on Windows.
|
|
97
|
+
If `pip` isn't found, use `python3 -m pip` on macOS/Linux or `py -m pip` on Windows.
|
|
94
98
|
|
|
95
|
-
> ⚠️
|
|
99
|
+
> ⚠️ The package is called **`bpp-format`**; the command and the Python import are `bpp`.
|
|
100
|
+
> `pip install bpp` installs an unrelated project.
|
|
96
101
|
|
|
97
|
-
Optional: `pip install
|
|
102
|
+
Optional: `pip install "bpp-format[stats]"` also installs tiktoken, for exact token counts instead
|
|
103
|
+
of estimates.
|
|
98
104
|
|
|
99
105
|
```bash
|
|
100
|
-
bpp --version # bpp 0.
|
|
106
|
+
bpp --version # bpp 0.4.0
|
|
101
107
|
```
|
|
102
108
|
|
|
103
109
|
### 2. Convert a file
|
|
@@ -113,15 +119,16 @@ This works with `.json`, `.yaml`, `.csv` and `.md` files. Want a sample to try?
|
|
|
113
119
|
### 3. Give it to your LLM
|
|
114
120
|
|
|
115
121
|
Open `quickstart.bpp`, then paste it into ChatGPT, Claude or your prompt template. If the model
|
|
116
|
-
hasn't seen the format before, add `--primer`. It prepends a one-line explanation
|
|
122
|
+
hasn't seen the format before, add `--primer`. It prepends a one-line explanation of the syntax
|
|
123
|
+
the file uses (40 tokens here, at most ~110):
|
|
117
124
|
|
|
118
125
|
```bash
|
|
119
126
|
bpp quickstart.json --primer -o - # print to the terminal instead of writing a file
|
|
120
127
|
```
|
|
121
128
|
|
|
122
129
|
```
|
|
123
|
-
|
|
124
|
-
#
|
|
130
|
+
bpp4
|
|
131
|
+
# bpp4: JSON as 'key value' lines, 1-space indent nests. k[N]{a b}: N rows, values in column order, last one = rest of line.
|
|
125
132
|
store Downtown Branch
|
|
126
133
|
...
|
|
127
134
|
```
|
|
@@ -144,10 +151,10 @@ JSON (indent 2) 434 146 136 +0.0% +0.0%
|
|
|
144
151
|
JSON (minified) 271 82 81 -43.8% -40.4%
|
|
145
152
|
YAML 261 102 82 -30.1% -39.7%
|
|
146
153
|
bpp 163 53 48 -63.7% -64.7%
|
|
147
|
-
bpp + primer
|
|
154
|
+
bpp + primer 288 93 91 -36.3% -33.1%
|
|
148
155
|
```
|
|
149
156
|
|
|
150
|
-
On a file this small the primer
|
|
157
|
+
On a file this small the primer still costs more than minified JSON. Use it on larger inputs.
|
|
151
158
|
|
|
152
159
|
### From Python
|
|
153
160
|
|
|
@@ -159,12 +166,13 @@ data = bpp.loads(text) # bpp text -> Python object
|
|
|
159
166
|
|
|
160
167
|
data = bpp.load("config.yaml") # reads .json .yaml .csv .md .bpp
|
|
161
168
|
bpp.dump(data, "config.bpp") # format chosen by extension
|
|
169
|
+
text = bpp.encode_md(markdown) # Markdown text -> bpp (or the source itself if that is shorter)
|
|
162
170
|
```
|
|
163
171
|
|
|
164
172
|
## The format in one minute
|
|
165
173
|
|
|
166
174
|
```
|
|
167
|
-
|
|
175
|
+
bpp4
|
|
168
176
|
server
|
|
169
177
|
host 0.0.0.0
|
|
170
178
|
port 8080
|
|
@@ -185,16 +193,18 @@ regions [TR,DE,NL]
|
|
|
185
193
|
means the rows indented under a row are its `items`, with their own columns.
|
|
186
194
|
* **Optional columns:** `x?` sits in place, with `-` when absent. `x?=` appears only when present,
|
|
187
195
|
as `x=value`.
|
|
188
|
-
* **Trees:** `>steps` means rows indented under a row are its children
|
|
189
|
-
written.
|
|
196
|
+
* **Trees:** `>steps` means rows indented under a row are its children, and a bare `>` means the
|
|
197
|
+
same key as the table (`steps[6]{...}>`). This is how plans are written.
|
|
198
|
+
* **Multi-line text:** `|3` stands for the next 3 lines, taken as they are: no quotes, no `\n`
|
|
199
|
+
escapes.
|
|
190
200
|
* **Dictionary:** a long value repeated many times is written once as `&0 value` and referenced
|
|
191
201
|
as `*0`, in YAML anchor style. It is used only when it measurably saves tokens.
|
|
192
202
|
* **Strings** are quoted JSON-style only when they would be ambiguous. Unicode stays raw UTF-8.
|
|
193
203
|
|
|
194
|
-
A plan with dependencies (`examples/plan.json`: 1609 tokens as JSON,
|
|
204
|
+
A plan with dependencies (`examples/plan.json`: 1609 tokens as JSON, 634 as bpp):
|
|
195
205
|
|
|
196
206
|
```
|
|
197
|
-
steps[6]{id:str status priority deps:str owner?= note?= title}>
|
|
207
|
+
steps[6]{id:str status priority deps:str owner?= note?= title}>
|
|
198
208
|
1 done P1 [] owner=Ayşe Gereksinim analizi
|
|
199
209
|
1.1 done P1 [] Paydaş görüşmeleri
|
|
200
210
|
1.2 done P0 [] Regülasyon incelemesi (BDDK, PCI-DSS)
|
|
@@ -213,17 +223,22 @@ ORD-2026-00001 Ankara paid standard 2897.68 *4 Ayşe Arslan
|
|
|
213
223
|
SKU-940 1 1890.88 Laptop Standı
|
|
214
224
|
```
|
|
215
225
|
|
|
216
|
-
A Markdown checklist (`examples/project_plan.md`: 837 tokens as Markdown,
|
|
217
|
-
and nested lists become a tree; `[ ]` `[x]` `[/]` `[-]` become `todo` `done` `doing` `cancelled
|
|
226
|
+
A Markdown checklist (`examples/project_plan.md`: 837 tokens as Markdown, 758 as bpp). Headings
|
|
227
|
+
and nested lists become a tree; `[ ]` `[x]` `[/]` `[-]` become `todo` `done` `doing` `cancelled`,
|
|
228
|
+
and wrapped lines are joined:
|
|
218
229
|
|
|
219
230
|
```
|
|
220
|
-
steps[5]{status? note?= title}>
|
|
221
|
-
|
|
222
|
-
done
|
|
231
|
+
steps[5]{title}>{status? note?= title}>
|
|
232
|
+
Keşif ve envanter
|
|
233
|
+
done Mevcut iş akışlarının envanteri Toplam 312 Airflow DAG'i, 48 Spark işi ve 17 Hive veritabanı tespit edildi.
|
|
223
234
|
done Veri sahipleriyle görüşmeler
|
|
224
235
|
done Pazarlama analitiği
|
|
225
236
|
```
|
|
226
237
|
|
|
238
|
+
When the tree would be longer than the Markdown itself (short or prose-only files), `bpp x.md`
|
|
239
|
+
writes `bpp4 md` and then the source unchanged. By the encoder's token estimate, a Markdown file
|
|
240
|
+
never grows by more than that header line.
|
|
241
|
+
|
|
227
242
|
The full grammar and the measurement behind each rule are in [SPEC.md](https://github.com/E7lektronXF/bpp/blob/main/SPEC.md).
|
|
228
243
|
|
|
229
244
|
## Why it is smaller
|
|
@@ -238,6 +253,8 @@ encodings usually cost more tokens and hurt understanding. bpp saves tokens by:
|
|
|
238
253
|
gain is positive.
|
|
239
254
|
4. **Keeping structure explicit:** row counts (`[N]`), column names and indentation give the model
|
|
240
255
|
a frame to read against.
|
|
256
|
+
5. **Writing text as text.** Multi-line strings are raw lines (`|N`), not JSON strings full of
|
|
257
|
+
`\n` and `\"` escapes.
|
|
241
258
|
|
|
242
259
|
## Guarantees
|
|
243
260
|
|
|
@@ -245,24 +262,32 @@ encodings usually cost more tokens and hurt understanding. bpp saves tokens by:
|
|
|
245
262
|
|---|---|
|
|
246
263
|
| JSON / YAML | `loads(dumps(x)) == x`, types included (`1` ≠ `1.0`, `"42"` ≠ `42`, `null`). With `--keep-order`, the JSON text comes back identical, key order included. YAML comments and anchors are not data and are not kept. Dates stay strings. |
|
|
247
264
|
| CSV | Cells and column order come back byte for byte. Numbers are typed only when writing them back gives the same text (`007` and `1.50` stay strings). |
|
|
248
|
-
| Markdown | Structure is kept, formatting is not. Output is normalized Markdown that parses back to the same tree. |
|
|
265
|
+
| Markdown | Structure is kept, formatting is not (soft line breaks are joined). Output is normalized Markdown that parses back to the same tree. A file kept as `bpp4 md` comes back byte for byte. |
|
|
249
266
|
|
|
250
|
-
Backed by
|
|
267
|
+
Backed by 240 tests: edge cases (empty containers, 80-level nesting, delimiters inside strings,
|
|
251
268
|
multi-line text, Unicode, number-like strings) and hypothesis property tests on random JSON, CSV,
|
|
252
269
|
YAML and Markdown trees.
|
|
253
270
|
|
|
254
271
|
## Honest caveats
|
|
255
272
|
|
|
256
|
-
* **
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
`
|
|
260
|
-
|
|
273
|
+
* **Comprehension is measured, but only a little.** Two open-weight models on Groq's free tier
|
|
274
|
+
(gpt-oss-120b, 2 rounds; qwen3.8-27b, 1 round), so one question moves accuracy by 0.8–1.7
|
|
275
|
+
points. bpp stayed within the 2-point bar we set in advance. Not yet run on Claude, GPT or
|
|
276
|
+
Gemini; `python bench/run_qa.py` reproduces it with any of them (full tables:
|
|
277
|
+
[BENCHMARK §3](BENCHMARK.md#3-comprehension-benchmark-first-results)).
|
|
278
|
+
* **The logs example is hard for every format** (counting over 80 entries: 0–8 of 20 right).
|
|
279
|
+
It is also where the `*n` dictionary does its work; bpp got 5/20 there vs 8/20 for pretty JSON
|
|
280
|
+
on gpt-oss-120b. Output-limit cut-offs (7 of 66 requests on that model, counted as wrong) blur
|
|
281
|
+
the difference, so a `--no-refs` run is the next measurement.
|
|
282
|
+
* **The primer made no consistent difference:** −6.7 points on gpt-oss-120b (mostly logs
|
|
283
|
+
cut-offs), +1.7 on qwen (one question). Without it, bpp already met the bar.
|
|
261
284
|
* **Token counts are proxies.** They come from tiktoken `o200k_base` and Anthropic's older public
|
|
262
285
|
Claude tokenizer. Current Claude models use a different tokenizer. Set `ANTHROPIC_API_KEY` and
|
|
263
286
|
`bpp stats` adds real `count_tokens` numbers.
|
|
264
287
|
* **Small gains in some cases:** plain flat tables are only ~6% smaller than CSV on o200k (17% on
|
|
265
|
-
claude2).
|
|
288
|
+
claude2). On prose-heavy Markdown the tree saves 0.4–2% (this repository's README, SPEC,
|
|
289
|
+
BENCHMARK and RELEASING); the savings are in lists and checklists. The primer (40–110 tokens)
|
|
290
|
+
cancels the savings on documents of a few hundred tokens.
|
|
266
291
|
|
|
267
292
|
## Command reference
|
|
268
293
|
|
|
@@ -279,7 +304,7 @@ bpp stats data.json [--markdown]
|
|
|
279
304
|
|
|
280
305
|
| option | effect |
|
|
281
306
|
|---|---|
|
|
282
|
-
| `--primer` | Prepend a format explanation (`short
|
|
307
|
+
| `--primer` | Prepend a format explanation (`short`: only the syntax used, 40–110 tokens; `long`: ~155 tokens). |
|
|
283
308
|
| `--no-refs` | Disable the `&n`/`*n` dictionary. |
|
|
284
309
|
| `--keep-order` | Never reorder keys. By default a table may move a free-text column such as `title` to the end of each row, which changes JSON key order but not the data. Always on for CSV input. |
|
|
285
310
|
| `-o -` | Write to stdout. |
|
|
@@ -291,19 +316,19 @@ bpp stats data.json [--markdown]
|
|
|
291
316
|
|---|---|
|
|
292
317
|
| `bpp: command not found` | Use `python -m bpp ...`, or open a new terminal. |
|
|
293
318
|
| `pip: command not found` | `python3 -m pip install ...` (macOS/Linux) or `py -m pip install ...` (Windows). |
|
|
294
|
-
| `externally-managed-environment` | `pipx install
|
|
319
|
+
| `externally-managed-environment` | `pipx install bpp-format`, or install inside a virtualenv (`python3 -m venv .venv && . .venv/bin/activate`). |
|
|
295
320
|
| `cannot infer format` | Use one of these extensions: `.json .yaml .yml .csv .md .bpp`. |
|
|
296
321
|
| `... exists; use -o ...` | bpp refused to overwrite your original file. Pick another name with `-o`. |
|
|
297
322
|
|
|
298
|
-
Upgrade with `pip install --upgrade
|
|
299
|
-
with `pip uninstall bpp`.
|
|
323
|
+
Upgrade with `pip install --upgrade bpp-format`. Uninstall
|
|
324
|
+
with `pip uninstall bpp-format`.
|
|
300
325
|
|
|
301
326
|
## Development
|
|
302
327
|
|
|
303
328
|
```bash
|
|
304
329
|
git clone https://github.com/E7lektronXF/bpp.git && cd bpp
|
|
305
330
|
pip install -e ".[dev]" # + pytest, hypothesis, tiktoken
|
|
306
|
-
pytest -q #
|
|
331
|
+
pytest -q # 240 tests
|
|
307
332
|
python bench/run_tokens.py # regenerate the token benchmark
|
|
308
333
|
python bench/experiments.py # the design experiments behind SPEC.md
|
|
309
334
|
python bench/run_qa.py # comprehension benchmark (needs ANTHROPIC_API_KEY)
|
|
@@ -8,6 +8,11 @@ language models read with far fewer tokens. You can convert it back without losi
|
|
|
8
8
|
every example.
|
|
9
9
|
* **Lossless round trip:** `decode(encode(x)) == x` for JSON and YAML, with types preserved.
|
|
10
10
|
CSV cells come back byte for byte. Markdown plans keep their structure.
|
|
11
|
+
* **Same accuracy as JSON in a first comprehension test.** 6 datasets, 10 auto-graded questions
|
|
12
|
+
each, same data in every format. We set the bar before running: within 2 points of JSON counts
|
|
13
|
+
as "same accuracy". gpt-oss-120b: **84.2% from bpp vs 85.8% from pretty JSON** (2 of 120
|
|
14
|
+
questions). qwen3.8-27b: **80.0% from bpp and from minified JSON**. Small open models, small
|
|
15
|
+
samples; the caveats are [below](#honest-caveats).
|
|
11
16
|
* **Measured, not invented.** Every syntax choice was benchmarked on two tokenizers. There are no
|
|
12
17
|
made-up symbols or binary tricks, only patterns models already know from YAML, CSV, JSON and
|
|
13
18
|
TypeScript.
|
|
@@ -21,7 +26,7 @@ token counts, with no install needed.
|
|
|
21
26
|
```
|
|
22
27
|
JSON, pretty-printed (146 tokens) bpp (53 tokens)
|
|
23
28
|
|
|
24
|
-
{
|
|
29
|
+
{ bpp4
|
|
25
30
|
"store": "Downtown Branch", store Downtown Branch
|
|
26
31
|
"orders": [ orders[3]{id product qty price customer}
|
|
27
32
|
{"id": 1, "customer": "Alice Johnson", 1 Headphones 2 79.9 Alice Johnson
|
|
@@ -38,11 +43,11 @@ Token counts per format for six example files (o200k tokenizer; fewer is better)
|
|
|
38
43
|
|---|---:|---:|---:|---:|---:|---:|---|
|
|
39
44
|
| 60×10 table (CSV) | 5538 | 3562 | 4388 | 2277 | – | **2042** | −6% vs CSV |
|
|
40
45
|
| nested config (YAML) | 601 | 365 | 443 | 399 | – | **323** | −12% vs minified JSON |
|
|
41
|
-
| project plan with deps (JSON) | 1609 | 990 | 1212 | 1228 | – | **
|
|
42
|
-
| Markdown checklist plan |
|
|
46
|
+
| project plan with deps (JSON) | 1609 | 990 | 1212 | 1228 | – | **634** | −36% vs minified JSON |
|
|
47
|
+
| Markdown checklist plan | 1445 | 990 | 1083 | 1024 | 837 | **758** | −9% vs Markdown |
|
|
43
48
|
| API response with nested objects | 3524 | 2231 | 2636 | 2276 | – | **1151** | −48% vs minified JSON |
|
|
44
49
|
| repetitive logs | 5629 | 4109 | 4587 | 3348 | – | **1857** | −42% vs CSV |
|
|
45
|
-
| **total** |
|
|
50
|
+
| **total** | 18346 | 12247 | 14349 | 10552 | | **6765** | **−63% vs JSON, −36% vs TOON** |
|
|
46
51
|
|
|
47
52
|
Anthropic's published Claude tokenizer (`claude2`) shows the same picture: −62% vs JSON, −36% vs
|
|
48
53
|
TOON. The full tables, the method and the cases where bpp wins by less are in
|
|
@@ -55,18 +60,19 @@ TOON. The full tables, the method and the cases where bpp wins by less are in
|
|
|
55
60
|
You need Python 3.9 or newer (check with `python --version`). Then run:
|
|
56
61
|
|
|
57
62
|
```bash
|
|
58
|
-
pip install
|
|
63
|
+
pip install bpp-format
|
|
59
64
|
```
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
on macOS/Linux or `py -m pip` on Windows.
|
|
66
|
+
If `pip` isn't found, use `python3 -m pip` on macOS/Linux or `py -m pip` on Windows.
|
|
63
67
|
|
|
64
|
-
> ⚠️
|
|
68
|
+
> ⚠️ The package is called **`bpp-format`**; the command and the Python import are `bpp`.
|
|
69
|
+
> `pip install bpp` installs an unrelated project.
|
|
65
70
|
|
|
66
|
-
Optional: `pip install
|
|
71
|
+
Optional: `pip install "bpp-format[stats]"` also installs tiktoken, for exact token counts instead
|
|
72
|
+
of estimates.
|
|
67
73
|
|
|
68
74
|
```bash
|
|
69
|
-
bpp --version # bpp 0.
|
|
75
|
+
bpp --version # bpp 0.4.0
|
|
70
76
|
```
|
|
71
77
|
|
|
72
78
|
### 2. Convert a file
|
|
@@ -82,15 +88,16 @@ This works with `.json`, `.yaml`, `.csv` and `.md` files. Want a sample to try?
|
|
|
82
88
|
### 3. Give it to your LLM
|
|
83
89
|
|
|
84
90
|
Open `quickstart.bpp`, then paste it into ChatGPT, Claude or your prompt template. If the model
|
|
85
|
-
hasn't seen the format before, add `--primer`. It prepends a one-line explanation
|
|
91
|
+
hasn't seen the format before, add `--primer`. It prepends a one-line explanation of the syntax
|
|
92
|
+
the file uses (40 tokens here, at most ~110):
|
|
86
93
|
|
|
87
94
|
```bash
|
|
88
95
|
bpp quickstart.json --primer -o - # print to the terminal instead of writing a file
|
|
89
96
|
```
|
|
90
97
|
|
|
91
98
|
```
|
|
92
|
-
|
|
93
|
-
#
|
|
99
|
+
bpp4
|
|
100
|
+
# bpp4: JSON as 'key value' lines, 1-space indent nests. k[N]{a b}: N rows, values in column order, last one = rest of line.
|
|
94
101
|
store Downtown Branch
|
|
95
102
|
...
|
|
96
103
|
```
|
|
@@ -113,10 +120,10 @@ JSON (indent 2) 434 146 136 +0.0% +0.0%
|
|
|
113
120
|
JSON (minified) 271 82 81 -43.8% -40.4%
|
|
114
121
|
YAML 261 102 82 -30.1% -39.7%
|
|
115
122
|
bpp 163 53 48 -63.7% -64.7%
|
|
116
|
-
bpp + primer
|
|
123
|
+
bpp + primer 288 93 91 -36.3% -33.1%
|
|
117
124
|
```
|
|
118
125
|
|
|
119
|
-
On a file this small the primer
|
|
126
|
+
On a file this small the primer still costs more than minified JSON. Use it on larger inputs.
|
|
120
127
|
|
|
121
128
|
### From Python
|
|
122
129
|
|
|
@@ -128,12 +135,13 @@ data = bpp.loads(text) # bpp text -> Python object
|
|
|
128
135
|
|
|
129
136
|
data = bpp.load("config.yaml") # reads .json .yaml .csv .md .bpp
|
|
130
137
|
bpp.dump(data, "config.bpp") # format chosen by extension
|
|
138
|
+
text = bpp.encode_md(markdown) # Markdown text -> bpp (or the source itself if that is shorter)
|
|
131
139
|
```
|
|
132
140
|
|
|
133
141
|
## The format in one minute
|
|
134
142
|
|
|
135
143
|
```
|
|
136
|
-
|
|
144
|
+
bpp4
|
|
137
145
|
server
|
|
138
146
|
host 0.0.0.0
|
|
139
147
|
port 8080
|
|
@@ -154,16 +162,18 @@ regions [TR,DE,NL]
|
|
|
154
162
|
means the rows indented under a row are its `items`, with their own columns.
|
|
155
163
|
* **Optional columns:** `x?` sits in place, with `-` when absent. `x?=` appears only when present,
|
|
156
164
|
as `x=value`.
|
|
157
|
-
* **Trees:** `>steps` means rows indented under a row are its children
|
|
158
|
-
written.
|
|
165
|
+
* **Trees:** `>steps` means rows indented under a row are its children, and a bare `>` means the
|
|
166
|
+
same key as the table (`steps[6]{...}>`). This is how plans are written.
|
|
167
|
+
* **Multi-line text:** `|3` stands for the next 3 lines, taken as they are: no quotes, no `\n`
|
|
168
|
+
escapes.
|
|
159
169
|
* **Dictionary:** a long value repeated many times is written once as `&0 value` and referenced
|
|
160
170
|
as `*0`, in YAML anchor style. It is used only when it measurably saves tokens.
|
|
161
171
|
* **Strings** are quoted JSON-style only when they would be ambiguous. Unicode stays raw UTF-8.
|
|
162
172
|
|
|
163
|
-
A plan with dependencies (`examples/plan.json`: 1609 tokens as JSON,
|
|
173
|
+
A plan with dependencies (`examples/plan.json`: 1609 tokens as JSON, 634 as bpp):
|
|
164
174
|
|
|
165
175
|
```
|
|
166
|
-
steps[6]{id:str status priority deps:str owner?= note?= title}>
|
|
176
|
+
steps[6]{id:str status priority deps:str owner?= note?= title}>
|
|
167
177
|
1 done P1 [] owner=Ayşe Gereksinim analizi
|
|
168
178
|
1.1 done P1 [] Paydaş görüşmeleri
|
|
169
179
|
1.2 done P0 [] Regülasyon incelemesi (BDDK, PCI-DSS)
|
|
@@ -182,17 +192,22 @@ ORD-2026-00001 Ankara paid standard 2897.68 *4 Ayşe Arslan
|
|
|
182
192
|
SKU-940 1 1890.88 Laptop Standı
|
|
183
193
|
```
|
|
184
194
|
|
|
185
|
-
A Markdown checklist (`examples/project_plan.md`: 837 tokens as Markdown,
|
|
186
|
-
and nested lists become a tree; `[ ]` `[x]` `[/]` `[-]` become `todo` `done` `doing` `cancelled
|
|
195
|
+
A Markdown checklist (`examples/project_plan.md`: 837 tokens as Markdown, 758 as bpp). Headings
|
|
196
|
+
and nested lists become a tree; `[ ]` `[x]` `[/]` `[-]` become `todo` `done` `doing` `cancelled`,
|
|
197
|
+
and wrapped lines are joined:
|
|
187
198
|
|
|
188
199
|
```
|
|
189
|
-
steps[5]{status? note?= title}>
|
|
190
|
-
|
|
191
|
-
done
|
|
200
|
+
steps[5]{title}>{status? note?= title}>
|
|
201
|
+
Keşif ve envanter
|
|
202
|
+
done Mevcut iş akışlarının envanteri Toplam 312 Airflow DAG'i, 48 Spark işi ve 17 Hive veritabanı tespit edildi.
|
|
192
203
|
done Veri sahipleriyle görüşmeler
|
|
193
204
|
done Pazarlama analitiği
|
|
194
205
|
```
|
|
195
206
|
|
|
207
|
+
When the tree would be longer than the Markdown itself (short or prose-only files), `bpp x.md`
|
|
208
|
+
writes `bpp4 md` and then the source unchanged. By the encoder's token estimate, a Markdown file
|
|
209
|
+
never grows by more than that header line.
|
|
210
|
+
|
|
196
211
|
The full grammar and the measurement behind each rule are in [SPEC.md](https://github.com/E7lektronXF/bpp/blob/main/SPEC.md).
|
|
197
212
|
|
|
198
213
|
## Why it is smaller
|
|
@@ -207,6 +222,8 @@ encodings usually cost more tokens and hurt understanding. bpp saves tokens by:
|
|
|
207
222
|
gain is positive.
|
|
208
223
|
4. **Keeping structure explicit:** row counts (`[N]`), column names and indentation give the model
|
|
209
224
|
a frame to read against.
|
|
225
|
+
5. **Writing text as text.** Multi-line strings are raw lines (`|N`), not JSON strings full of
|
|
226
|
+
`\n` and `\"` escapes.
|
|
210
227
|
|
|
211
228
|
## Guarantees
|
|
212
229
|
|
|
@@ -214,24 +231,32 @@ encodings usually cost more tokens and hurt understanding. bpp saves tokens by:
|
|
|
214
231
|
|---|---|
|
|
215
232
|
| JSON / YAML | `loads(dumps(x)) == x`, types included (`1` ≠ `1.0`, `"42"` ≠ `42`, `null`). With `--keep-order`, the JSON text comes back identical, key order included. YAML comments and anchors are not data and are not kept. Dates stay strings. |
|
|
216
233
|
| CSV | Cells and column order come back byte for byte. Numbers are typed only when writing them back gives the same text (`007` and `1.50` stay strings). |
|
|
217
|
-
| Markdown | Structure is kept, formatting is not. Output is normalized Markdown that parses back to the same tree. |
|
|
234
|
+
| Markdown | Structure is kept, formatting is not (soft line breaks are joined). Output is normalized Markdown that parses back to the same tree. A file kept as `bpp4 md` comes back byte for byte. |
|
|
218
235
|
|
|
219
|
-
Backed by
|
|
236
|
+
Backed by 240 tests: edge cases (empty containers, 80-level nesting, delimiters inside strings,
|
|
220
237
|
multi-line text, Unicode, number-like strings) and hypothesis property tests on random JSON, CSV,
|
|
221
238
|
YAML and Markdown trees.
|
|
222
239
|
|
|
223
240
|
## Honest caveats
|
|
224
241
|
|
|
225
|
-
* **
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
`
|
|
229
|
-
|
|
242
|
+
* **Comprehension is measured, but only a little.** Two open-weight models on Groq's free tier
|
|
243
|
+
(gpt-oss-120b, 2 rounds; qwen3.8-27b, 1 round), so one question moves accuracy by 0.8–1.7
|
|
244
|
+
points. bpp stayed within the 2-point bar we set in advance. Not yet run on Claude, GPT or
|
|
245
|
+
Gemini; `python bench/run_qa.py` reproduces it with any of them (full tables:
|
|
246
|
+
[BENCHMARK §3](BENCHMARK.md#3-comprehension-benchmark-first-results)).
|
|
247
|
+
* **The logs example is hard for every format** (counting over 80 entries: 0–8 of 20 right).
|
|
248
|
+
It is also where the `*n` dictionary does its work; bpp got 5/20 there vs 8/20 for pretty JSON
|
|
249
|
+
on gpt-oss-120b. Output-limit cut-offs (7 of 66 requests on that model, counted as wrong) blur
|
|
250
|
+
the difference, so a `--no-refs` run is the next measurement.
|
|
251
|
+
* **The primer made no consistent difference:** −6.7 points on gpt-oss-120b (mostly logs
|
|
252
|
+
cut-offs), +1.7 on qwen (one question). Without it, bpp already met the bar.
|
|
230
253
|
* **Token counts are proxies.** They come from tiktoken `o200k_base` and Anthropic's older public
|
|
231
254
|
Claude tokenizer. Current Claude models use a different tokenizer. Set `ANTHROPIC_API_KEY` and
|
|
232
255
|
`bpp stats` adds real `count_tokens` numbers.
|
|
233
256
|
* **Small gains in some cases:** plain flat tables are only ~6% smaller than CSV on o200k (17% on
|
|
234
|
-
claude2).
|
|
257
|
+
claude2). On prose-heavy Markdown the tree saves 0.4–2% (this repository's README, SPEC,
|
|
258
|
+
BENCHMARK and RELEASING); the savings are in lists and checklists. The primer (40–110 tokens)
|
|
259
|
+
cancels the savings on documents of a few hundred tokens.
|
|
235
260
|
|
|
236
261
|
## Command reference
|
|
237
262
|
|
|
@@ -248,7 +273,7 @@ bpp stats data.json [--markdown]
|
|
|
248
273
|
|
|
249
274
|
| option | effect |
|
|
250
275
|
|---|---|
|
|
251
|
-
| `--primer` | Prepend a format explanation (`short
|
|
276
|
+
| `--primer` | Prepend a format explanation (`short`: only the syntax used, 40–110 tokens; `long`: ~155 tokens). |
|
|
252
277
|
| `--no-refs` | Disable the `&n`/`*n` dictionary. |
|
|
253
278
|
| `--keep-order` | Never reorder keys. By default a table may move a free-text column such as `title` to the end of each row, which changes JSON key order but not the data. Always on for CSV input. |
|
|
254
279
|
| `-o -` | Write to stdout. |
|
|
@@ -260,19 +285,19 @@ bpp stats data.json [--markdown]
|
|
|
260
285
|
|---|---|
|
|
261
286
|
| `bpp: command not found` | Use `python -m bpp ...`, or open a new terminal. |
|
|
262
287
|
| `pip: command not found` | `python3 -m pip install ...` (macOS/Linux) or `py -m pip install ...` (Windows). |
|
|
263
|
-
| `externally-managed-environment` | `pipx install
|
|
288
|
+
| `externally-managed-environment` | `pipx install bpp-format`, or install inside a virtualenv (`python3 -m venv .venv && . .venv/bin/activate`). |
|
|
264
289
|
| `cannot infer format` | Use one of these extensions: `.json .yaml .yml .csv .md .bpp`. |
|
|
265
290
|
| `... exists; use -o ...` | bpp refused to overwrite your original file. Pick another name with `-o`. |
|
|
266
291
|
|
|
267
|
-
Upgrade with `pip install --upgrade
|
|
268
|
-
with `pip uninstall bpp`.
|
|
292
|
+
Upgrade with `pip install --upgrade bpp-format`. Uninstall
|
|
293
|
+
with `pip uninstall bpp-format`.
|
|
269
294
|
|
|
270
295
|
## Development
|
|
271
296
|
|
|
272
297
|
```bash
|
|
273
298
|
git clone https://github.com/E7lektronXF/bpp.git && cd bpp
|
|
274
299
|
pip install -e ".[dev]" # + pytest, hypothesis, tiktoken
|
|
275
|
-
pytest -q #
|
|
300
|
+
pytest -q # 240 tests
|
|
276
301
|
python bench/run_tokens.py # regenerate the token benchmark
|
|
277
302
|
python bench/experiments.py # the design experiments behind SPEC.md
|
|
278
303
|
python bench/run_qa.py # comprehension benchmark (needs ANTHROPIC_API_KEY)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bpp-format"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "A token-efficient text format for feeding structured data and plans to LLMs, with a lossless JSON/YAML/CSV converter."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
text = bpp.dumps({"users": [{"id": 1, "name": "Ayşe"}]}) # -> .bpp text
|
|
5
5
|
data = bpp.loads(text) # -> Python objects
|
|
6
6
|
data = bpp.load("config.yaml") # any supported file (.json .yaml .csv .md .bpp)
|
|
7
|
+
text = bpp.encode_md(markdown_text) # Markdown -> .bpp (keeps the source if that is shorter)
|
|
7
8
|
bpp.dump(data, "config.bpp") # format from the extension
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
12
|
-
from .decoder import decode
|
|
13
|
-
from .encoder import encode
|
|
13
|
+
from .decoder import decode, md_source
|
|
14
|
+
from .encoder import encode, encode_md
|
|
14
15
|
from .lexer import BppError
|
|
15
16
|
|
|
16
|
-
__version__ = "0.
|
|
17
|
-
__all__ = ["encode", "
|
|
17
|
+
__version__ = "0.4.0"
|
|
18
|
+
__all__ = ["encode", "encode_md", "decode", "md_source", "dumps", "loads", "load", "dump",
|
|
19
|
+
"BppError", "__version__"]
|
|
18
20
|
|
|
19
21
|
dumps = encode
|
|
20
22
|
loads = decode
|