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.
Files changed (32) hide show
  1. {bpp_format-0.3.0 → bpp_format-0.4.0}/PKG-INFO +64 -39
  2. {bpp_format-0.3.0 → bpp_format-0.4.0}/README.md +63 -38
  3. {bpp_format-0.3.0 → bpp_format-0.4.0}/pyproject.toml +1 -1
  4. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/__init__.py +6 -4
  5. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/cli.py +52 -21
  6. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/decoder.py +122 -70
  7. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/encoder.py +210 -68
  8. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/estimate.py +2 -2
  9. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/lexer.py +22 -11
  10. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/markdown.py +80 -3
  11. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/PKG-INFO +64 -39
  12. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/SOURCES.txt +1 -0
  13. bpp_format-0.4.0/tests/test_bpp4.py +234 -0
  14. {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_cli.py +20 -3
  15. {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_js_parity.py +40 -8
  16. {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_markdown.py +6 -2
  17. {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_syntax.py +7 -7
  18. {bpp_format-0.3.0 → bpp_format-0.4.0}/LICENSE +0 -0
  19. {bpp_format-0.3.0 → bpp_format-0.4.0}/setup.cfg +0 -0
  20. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/__main__.py +0 -0
  21. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/formats.py +0 -0
  22. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp/tokens.py +0 -0
  23. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/dependency_links.txt +0 -0
  24. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/entry_points.txt +0 -0
  25. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/requires.txt +0 -0
  26. {bpp_format-0.3.0 → bpp_format-0.4.0}/src/bpp_format.egg-info/top_level.txt +0 -0
  27. {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_bench.py +0 -0
  28. {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_formats.py +0 -0
  29. {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_property.py +0 -0
  30. {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_roundtrip.py +0 -0
  31. {bpp_format-0.3.0 → bpp_format-0.4.0}/tests/test_site.py +0 -0
  32. {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.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
- { bpp3
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 | – | **636** | −36% vs minified JSON |
73
- | Markdown checklist plan | 1501 | 1025 | 1119 | 1093 | 837 | **792** | −5% vs Markdown |
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** | 18402 | 12282 | 14385 | 10621 | | **6801** | **−63% vs JSON, −36% vs TOON** |
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 https://github.com/E7lektronXF/bpp/archive/HEAD.zip
94
+ pip install bpp-format
90
95
  ```
91
96
 
92
- This installs straight from GitHub. You don't need git. If `pip` isn't found, use `python3 -m pip`
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
- > ⚠️ **Don't run `pip install bpp`.** The package called `bpp` on PyPI is an unrelated project.
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 tiktoken` to get exact token counts instead of estimates.
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.3.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 (~85 tokens):
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
- bpp3
124
- # bpp3: JSON as 'key value' lines, 1-space indent nests. k[N]{a b}: N rows of values in column order, last column = rest of line; x? = optional ('-' if absent), x?= columns appear as x=v. "..." = JSON string, *n = &n.
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 402 137 137 -6.2% +0.7%
154
+ bpp + primer 288 93 91 -36.3% -33.1%
148
155
  ```
149
156
 
150
- On a file this small the primer eats most of the savings. Use it on larger inputs.
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
- bpp3
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. This is how plans are
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, 636 as bpp):
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}>steps
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, 792 as bpp). Headings
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}>steps
221
- - Keşif ve envanter
222
- done note="Toplam 312 Airflow DAG'i, 48 Spark işi ve 17 Hive veritabanı tespit edildi." Mevcut iş akışlarının envanteri
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 217 tests: edge cases (empty containers, 80-level nesting, delimiters inside strings,
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
- * **Understanding is not measured yet.** Token savings are measured. Whether models answer
257
- questions about bpp as accurately as about JSON is not yet known. A ready-to-run comprehension
258
- benchmark (10 auto-graded questions per dataset, every format) is included:
259
- `ANTHROPIC_API_KEY=... python bench/run_qa.py`. The biggest risk is the dictionary: the model
260
- has to resolve `*3` to its definition.
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). The primer (~85 tokens) cancels the savings on documents of a few hundred tokens.
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` ~85, `long` ~135 tokens). |
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 https://github.com/E7lektronXF/bpp/archive/HEAD.zip`, or install inside a virtualenv (`python3 -m venv .venv && . .venv/bin/activate`). |
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 https://github.com/E7lektronXF/bpp/archive/HEAD.zip`. Uninstall
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 # 217 tests
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
- { bpp3
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 | – | **636** | −36% vs minified JSON |
42
- | Markdown checklist plan | 1501 | 1025 | 1119 | 1093 | 837 | **792** | −5% vs Markdown |
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** | 18402 | 12282 | 14385 | 10621 | | **6801** | **−63% vs JSON, −36% vs TOON** |
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 https://github.com/E7lektronXF/bpp/archive/HEAD.zip
63
+ pip install bpp-format
59
64
  ```
60
65
 
61
- This installs straight from GitHub. You don't need git. If `pip` isn't found, use `python3 -m pip`
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
- > ⚠️ **Don't run `pip install bpp`.** The package called `bpp` on PyPI is an unrelated project.
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 tiktoken` to get exact token counts instead of estimates.
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.3.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 (~85 tokens):
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
- bpp3
93
- # bpp3: JSON as 'key value' lines, 1-space indent nests. k[N]{a b}: N rows of values in column order, last column = rest of line; x? = optional ('-' if absent), x?= columns appear as x=v. "..." = JSON string, *n = &n.
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 402 137 137 -6.2% +0.7%
123
+ bpp + primer 288 93 91 -36.3% -33.1%
117
124
  ```
118
125
 
119
- On a file this small the primer eats most of the savings. Use it on larger inputs.
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
- bpp3
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. This is how plans are
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, 636 as bpp):
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}>steps
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, 792 as bpp). Headings
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}>steps
190
- - Keşif ve envanter
191
- done note="Toplam 312 Airflow DAG'i, 48 Spark işi ve 17 Hive veritabanı tespit edildi." Mevcut iş akışlarının envanteri
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 217 tests: edge cases (empty containers, 80-level nesting, delimiters inside strings,
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
- * **Understanding is not measured yet.** Token savings are measured. Whether models answer
226
- questions about bpp as accurately as about JSON is not yet known. A ready-to-run comprehension
227
- benchmark (10 auto-graded questions per dataset, every format) is included:
228
- `ANTHROPIC_API_KEY=... python bench/run_qa.py`. The biggest risk is the dictionary: the model
229
- has to resolve `*3` to its definition.
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). The primer (~85 tokens) cancels the savings on documents of a few hundred tokens.
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` ~85, `long` ~135 tokens). |
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 https://github.com/E7lektronXF/bpp/archive/HEAD.zip`, or install inside a virtualenv (`python3 -m venv .venv && . .venv/bin/activate`). |
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 https://github.com/E7lektronXF/bpp/archive/HEAD.zip`. Uninstall
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 # 217 tests
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.3.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.3.0"
17
- __all__ = ["encode", "decode", "dumps", "loads", "load", "dump", "BppError", "__version__"]
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