pycontextfold 0.1.0__tar.gz → 0.1.2__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 (59) hide show
  1. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/Cargo.lock +5 -5
  2. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/Cargo.toml +6 -6
  3. pycontextfold-0.1.2/PKG-INFO +290 -0
  4. pycontextfold-0.1.2/README.md +230 -0
  5. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/pyproject.toml +34 -1
  6. pycontextfold-0.1.2/src/pycontextfold/bench/__init__.py +85 -0
  7. pycontextfold-0.1.2/src/pycontextfold/bench/__main__.py +8 -0
  8. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/__init__.py +64 -0
  9. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/base.py +133 -0
  10. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/baseline.py +42 -0
  11. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/contextfold.py +95 -0
  12. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/openanchor.py +46 -0
  13. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/pyagenthound.py +71 -0
  14. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/pyapicheck.py +68 -0
  15. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/pyinferencemanager.py +65 -0
  16. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/pystreamai.py +51 -0
  17. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/pystreammcp.py +85 -0
  18. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/pystreampdf.py +91 -0
  19. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/pytokencalc.py +82 -0
  20. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/pyvectorhound.py +63 -0
  21. pycontextfold-0.1.2/src/pycontextfold/bench/adapters/registry.py +122 -0
  22. pycontextfold-0.1.2/src/pycontextfold/bench/capabilities.py +124 -0
  23. pycontextfold-0.1.2/src/pycontextfold/bench/chunking.py +187 -0
  24. pycontextfold-0.1.2/src/pycontextfold/bench/cli.py +188 -0
  25. pycontextfold-0.1.2/src/pycontextfold/bench/config.py +85 -0
  26. pycontextfold-0.1.2/src/pycontextfold/bench/dataset.py +166 -0
  27. pycontextfold-0.1.2/src/pycontextfold/bench/gates.py +164 -0
  28. pycontextfold-0.1.2/src/pycontextfold/bench/metrics.py +176 -0
  29. pycontextfold-0.1.2/src/pycontextfold/bench/models.py +350 -0
  30. pycontextfold-0.1.2/src/pycontextfold/bench/report.py +320 -0
  31. pycontextfold-0.1.2/src/pycontextfold/bench/runner.py +270 -0
  32. pycontextfold-0.1.2/src/pycontextfold/bench/runs.py +215 -0
  33. pycontextfold-0.1.2/src/pycontextfold/bench/tokens.py +99 -0
  34. pycontextfold-0.1.0/PKG-INFO +0 -189
  35. pycontextfold-0.1.0/README.md +0 -156
  36. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/LICENSE +0 -0
  37. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/Cargo.toml +0 -0
  38. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/analyzer.rs +0 -0
  39. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/edit.rs +0 -0
  40. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/engine.rs +0 -0
  41. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/error.rs +0 -0
  42. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/lang.rs +0 -0
  43. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/lib.rs +0 -0
  44. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/model.rs +0 -0
  45. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/profile.rs +0 -0
  46. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/rules.rs +0 -0
  47. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/tokenizer.rs +0 -0
  48. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/src/transform.rs +0 -0
  49. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/tests/core_tests.rs +0 -0
  50. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/tests/properties.proptest-regressions +0 -0
  51. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-core/tests/properties.rs +0 -0
  52. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-parsers/Cargo.toml +0 -0
  53. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-parsers/src/langspec.rs +0 -0
  54. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-parsers/src/lib.rs +0 -0
  55. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-parsers/tests/parsing.rs +0 -0
  56. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-py/Cargo.toml +0 -0
  57. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/crates/contextfold-py/src/lib.rs +0 -0
  58. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/src/pycontextfold/__init__.py +0 -0
  59. {pycontextfold-0.1.0 → pycontextfold-0.1.2}/src/pycontextfold/py.typed +0 -0
@@ -254,7 +254,7 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
254
254
 
255
255
  [[package]]
256
256
  name = "contextfold-bench"
257
- version = "0.1.0"
257
+ version = "0.1.2"
258
258
  dependencies = [
259
259
  "contextfold-core",
260
260
  "contextfold-parsers",
@@ -263,7 +263,7 @@ dependencies = [
263
263
 
264
264
  [[package]]
265
265
  name = "contextfold-cli"
266
- version = "0.1.0"
266
+ version = "0.1.2"
267
267
  dependencies = [
268
268
  "anyhow",
269
269
  "clap",
@@ -274,7 +274,7 @@ dependencies = [
274
274
 
275
275
  [[package]]
276
276
  name = "contextfold-core"
277
- version = "0.1.0"
277
+ version = "0.1.2"
278
278
  dependencies = [
279
279
  "proptest",
280
280
  "serde",
@@ -284,7 +284,7 @@ dependencies = [
284
284
 
285
285
  [[package]]
286
286
  name = "contextfold-parsers"
287
- version = "0.1.0"
287
+ version = "0.1.2"
288
288
  dependencies = [
289
289
  "contextfold-core",
290
290
  "serde",
@@ -299,7 +299,7 @@ dependencies = [
299
299
 
300
300
  [[package]]
301
301
  name = "contextfold-py"
302
- version = "0.1.0"
302
+ version = "0.1.2"
303
303
  dependencies = [
304
304
  "contextfold-core",
305
305
  "contextfold-parsers",
@@ -4,16 +4,16 @@ resolver = "2"
4
4
  exclude = ["fuzz"]
5
5
 
6
6
  [workspace.package]
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  edition = "2021"
9
- rust-version = "1.82"
10
- authors = ["Georgi Mullassery <mullassery@gmail.com>"]
11
- license = "Apache-2.0"
9
+ rust-version = "1.75"
10
+ license = "MIT"
12
11
  repository = "https://github.com/Mullassery/PyContextFold"
12
+ authors = ["Georgi Mullassery"]
13
13
 
14
14
  [workspace.dependencies]
15
- contextfold-core = { path = "crates/contextfold-core", version = "0.1.0" }
16
- contextfold-parsers = { path = "crates/contextfold-parsers", version = "0.1.0" }
15
+ contextfold-core = { path = "crates/contextfold-core", version = "0.1.2" }
16
+ contextfold-parsers = { path = "crates/contextfold-parsers", version = "0.1.2" }
17
17
 
18
18
  serde = { version = "1.0", features = ["derive"] }
19
19
  serde_json = "1.0"
@@ -0,0 +1,290 @@
1
+ Metadata-Version: 2.4
2
+ Name: pycontextfold
3
+ Version: 0.1.2
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Environment :: Console
6
+ Classifier: Intended Audience :: Developers
7
+ Classifier: Operating System :: OS Independent
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Rust
14
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Classifier: Typing :: Typed
17
+ Requires-Dist: pyagenthound ; extra == 'agents'
18
+ Requires-Dist: pyapicheck ; extra == 'api'
19
+ Requires-Dist: pytest>=7.0 ; extra == 'dev'
20
+ Requires-Dist: maturin>=1.0 ; extra == 'dev'
21
+ Requires-Dist: ruff>=0.5.0 ; extra == 'dev'
22
+ Requires-Dist: pystreammcp ; extra == 'discovery'
23
+ Requires-Dist: pytokencalc ; extra == 'ecosystem'
24
+ Requires-Dist: pystreampdf ; extra == 'ecosystem'
25
+ Requires-Dist: pyagenthound ; extra == 'ecosystem'
26
+ Requires-Dist: pyapicheck ; extra == 'ecosystem'
27
+ Requires-Dist: pyvectorhound ; extra == 'ecosystem'
28
+ Requires-Dist: pyinferencemanager ; extra == 'ecosystem'
29
+ Requires-Dist: pystreamai ; extra == 'ecosystem'
30
+ Requires-Dist: openanchor ; extra == 'ecosystem'
31
+ Requires-Dist: pystreammcp ; extra == 'ecosystem'
32
+ Requires-Dist: pyinferencemanager ; extra == 'inference'
33
+ Requires-Dist: pystreamai ; extra == 'inference'
34
+ Requires-Dist: openanchor ; extra == 'middleware'
35
+ Requires-Dist: pystreampdf ; extra == 'pdf'
36
+ Requires-Dist: pyvectorhound ; extra == 'retrieval'
37
+ Requires-Dist: pytokencalc ; extra == 'tokens'
38
+ Provides-Extra: agents
39
+ Provides-Extra: api
40
+ Provides-Extra: dev
41
+ Provides-Extra: discovery
42
+ Provides-Extra: ecosystem
43
+ Provides-Extra: inference
44
+ Provides-Extra: middleware
45
+ Provides-Extra: pdf
46
+ Provides-Extra: retrieval
47
+ Provides-Extra: tokens
48
+ License-File: LICENSE
49
+ Summary: Structure-aware context optimization for LLM consumption. Shrink source code, documentation, logs and structured text before sending them to an LLM. Typed Python API with a fast, deterministic Rust core, Tree-sitter parsing, conservative profiles, preservation policies, budget-aware folding and transformation reports.
50
+ Keywords: llm,context,compression,tree-sitter,token-budget,code-analysis,local-first,rust,pyo3,context-optimization,prompt-engineering,log-reduction,markdown
51
+ Author-email: Georgi Mammen Mullassery <mullassery@gmail.com>
52
+ License-Expression: Apache-2.0
53
+ Requires-Python: >=3.10
54
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
55
+ Project-URL: Documentation, https://github.com/Mullassery/PyContextFold/blob/main/README.md
56
+ Project-URL: Homepage, https://github.com/Mullassery/PyContextFold
57
+ Project-URL: Issues, https://github.com/Mullassery/PyContextFold/issues
58
+ Project-URL: Repository, https://github.com/Mullassery/PyContextFold.git
59
+
60
+ # PyContextFold
61
+
62
+ [![PyPI](https://img.shields.io/pypi/v/pycontextfold.svg)](https://pypi.org/project/pycontextfold/)
63
+ [![Python versions](https://img.shields.io/pypi/pyversions/pycontextfold.svg)](https://pypi.org/project/pycontextfold/)
64
+ [![CI](https://github.com/Mullassery/PyContextFold/actions/workflows/tests.yml/badge.svg)](https://github.com/Mullassery/PyContextFold/actions/workflows/tests.yml)
65
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
66
+
67
+ **A Python library for structure-aware context optimization for LLM consumption.**
68
+
69
+ PyContextFold shrinks source code, technical documentation, logs and structured
70
+ text before you send them to an LLM — cutting token cost while keeping every
71
+ transformation explicit, configurable and reportable. It is a local-first
72
+ `pip install` with a fast, deterministic Rust core underneath, and it never
73
+ sends your content anywhere.
74
+
75
+ It is a *transformation engine for content you already have* — not an inference
76
+ orchestrator, retrieval engine, token-counting library, PDF processor, or agent
77
+ framework.
78
+
79
+ ## Install
80
+
81
+ ```bash
82
+ pip install pycontextfold
83
+ ```
84
+
85
+ Requires Python ≥ 3.10 (the wheel is built `abi3-py310`). The core, the CLI and
86
+ the offline benchmark need nothing else. Optional ecosystem adapters activate
87
+ when their project is importable; install them individually, e.g.
88
+ `pip install "pycontextfold[tokens]"`, `[pdf]`, `[agents]`, or `[ecosystem]` for
89
+ all of them.
90
+
91
+ ## Quick start
92
+
93
+ ```python
94
+ import pycontextfold as pcf
95
+
96
+ result = pcf.fold(source, profile="safe", path="app.py")
97
+ print(result.output) # transformed text
98
+ print(result.report.detected_language) # "python"
99
+ print(result.report.reduction_ratio) # e.g. 0.19
100
+ print(result.report.syntax_valid) # True
101
+ print(result.report.to_dict()) # serialisable report
102
+ ```
103
+
104
+ Fold a file without ever modifying it:
105
+
106
+ ```python
107
+ result = pcf.fold_file("app.py", profile="code-review")
108
+ open("app.folded.py", "w").write(result.output)
109
+ ```
110
+
111
+ Introspection and full control:
112
+
113
+ ```python
114
+ pcf.profiles() # ['budgeted', 'code-review', ...]
115
+ pcf.profile_info("safe") # {name, description, loss, ...}
116
+ pcf.supported_languages() # [...]
117
+ pcf.detect_language("fn main() {}", path="a.rs") # "rust"
118
+
119
+ # Typed request object with budgets, protection and a caller-supplied tokenizer
120
+ req = pcf.FoldRequest(
121
+ source,
122
+ profile="budgeted",
123
+ language="python",
124
+ budget_bytes=4096,
125
+ protected_ranges=[(0, 128)], # keep the license header
126
+ protected_line_markers=["contextfold:keep"],
127
+ )
128
+ res = pcf.Engine().fold_request(req)
129
+ ```
130
+
131
+ Errors are typed — `UnknownProfileError`, `InvalidConfigError`,
132
+ `BudgetNotMetError` — all subclasses of `ContextFoldError`.
133
+
134
+ ## What it owns (and what it deliberately does not)
135
+
136
+ **Owns:** content-aware parsing, structural transformations, redundancy
137
+ reduction, configurable preservation policies, budget-constrained folding,
138
+ validation of transformed output, and transformation reports.
139
+
140
+ **Does not own** (and never re-implements, imports, or depends on): provider
141
+ tokenization/cost (PyTokenCalc), context discovery/retrieval (PyStreamMCP),
142
+ inference orchestration (PyInferenceManager), PDF extraction (PyStreamPDF),
143
+ vector retrieval evaluation (PyVectorHound), agent tracing (PyAgentHound), or
144
+ OpenAnchor's token-intelligence middleware. Stable *interfaces* are exposed for
145
+ optional integration, but there are no cross-repository dependencies.
146
+
147
+ ## Guarantees
148
+
149
+ - **Deterministic.** The same input, profile, engine version and registry
150
+ produce byte-identical output.
151
+ - **Conservative by default.** The `safe` profile never removes executable
152
+ statements, imports, conditions or error handling.
153
+ - **Honest about loss.** Lossy rules are labelled in the report. The engine
154
+ **never claims semantic equivalence** — only, optionally, syntactic validity.
155
+ - **Local-first.** No network access. Source content is never logged by default.
156
+ - **Non-destructive.** Inputs are never overwritten unless you explicitly opt in.
157
+
158
+ ## Profiles
159
+
160
+ | Profile | Lossy | Summary |
161
+ |------------------|:-----:|---------|
162
+ | `safe` | no | Remove comments, collapse blank-line runs, trim trailing whitespace. Never removes code, imports, conditions or error handling. Docstrings preserved. |
163
+ | `code-review` | no | Preserve implementation logic **and** comments/docstrings; whitespace-only normalisation. |
164
+ | `interface-only` | yes | Keep imports, declarations, signatures and docstrings; replace function bodies with valid placeholders. |
165
+ | `documentation` | yes | Reduce redundant Markdown/prose; preserve headings, code fences, links and normative keywords. |
166
+ | `logs` | yes | Collapse consecutive duplicate log events, retaining configured counts. |
167
+ | `budgeted` | yes | Aggressive deterministic ordering to reach a size/token budget. |
168
+
169
+ Profiles are versioned (`PROFILE_SCHEMA_VERSION`, surfaced in every report).
170
+ See [`docs/PROFILES.md`](docs/PROFILES.md) for per-profile guarantees.
171
+
172
+ ## Token budgets
173
+
174
+ PyContextFold accepts a **target budget** but does not ship a provider
175
+ tokenizer catalogue. Supply a tokenizer implementing `count(text) -> int`, or
176
+ use byte/character budgets (clearly reported as approximations, never as token
177
+ counts):
178
+
179
+ ```python
180
+ class MyTokenizer:
181
+ name = "my-tok"
182
+ def count(self, text: str) -> int:
183
+ ...
184
+
185
+ req = pcf.FoldRequest(source, profile="budgeted",
186
+ budget_tokens=500, tokenizer=MyTokenizer())
187
+ res = pcf.Engine().fold_request(req)
188
+ ```
189
+
190
+ When a budget cannot be met under the configured loss policy, the report
191
+ contains `warnings: ["budget_not_met"]`; the engine does not silently drop code.
192
+
193
+ ## Benchmarking (offline, dependency-light)
194
+
195
+ The `pycontextfold.bench` subpackage measures the trade-offs of folding: how much
196
+ is removed (size/character/token reduction) versus how much matters (ground-truth
197
+ preservation). It runs with nothing else installed and never touches the network.
198
+
199
+ ```python
200
+ from pycontextfold import bench
201
+
202
+ ds = bench.dataset.synthetic_dataset()
203
+ reg = bench.adapters.default_registry()
204
+ adapter = reg.get("contextfold")
205
+
206
+ for doc in ds:
207
+ result = adapter.optimize(bench.OptimizationRequest(doc, method="safe"))
208
+ metrics = bench.metrics.document_metrics(doc, result, ds.ground_truth_for(doc.id))
209
+ print(doc.id, {m.name: round(m.value, 3) for m in metrics if m.available})
210
+ ```
211
+
212
+ Optional ecosystem adapters (PyTokenCalc, PyStreamPDF, PyVectorHound, ...) are
213
+ capability-declared and imported lazily; a requested-but-missing capability is
214
+ reported as *unavailable* rather than silently substituted. See
215
+ [`docs/ROADMAP.md`](docs/ROADMAP.md) and
216
+ [`docs/ECOSYSTEM_ASSESSMENT.md`](docs/ECOSYSTEM_ASSESSMENT.md).
217
+
218
+ Run a whole experiment and emit a single-file HTML report (no CDN, fully
219
+ offline):
220
+
221
+ ```python
222
+ from pycontextfold import bench
223
+
224
+ run = bench.run_experiment(
225
+ bench.dataset.synthetic_dataset(),
226
+ bench.ExperimentConfig(name="demo", dataset="synthetic",
227
+ adapters=("baseline", "contextfold")),
228
+ )
229
+ bench.write_report(run, "report.html") # self-contained, deterministic
230
+ ```
231
+
232
+ There is also a CLI (offline, stdlib only):
233
+
234
+ ```bash
235
+ pycontextfold-bench adapters # list + availability
236
+ pycontextfold-bench run --config exp.json --report report.html
237
+ pycontextfold-bench run --config exp.json --gate # exit 1 on gate failure
238
+ pycontextfold-bench report run.json --out report.html
239
+ ```
240
+
241
+ Gates turn a run into a regression check — an aggregated metric below its
242
+ threshold, or an unavailable metric, fails the run.
243
+
244
+ ## Command-line tool (optional)
245
+
246
+ Installing the package gives you the Python library. The repository also ships a
247
+ standalone `contextfold` CLI and a Rust library for non-Python use:
248
+
249
+ ```bash
250
+ cargo install --path crates/contextfold-cli
251
+ echo 'print("hi") # comment' | contextfold -p safe
252
+ contextfold app.py -p safe --report report.json
253
+ ```
254
+
255
+ ## Architecture
256
+
257
+ ```
258
+ src/pycontextfold Python package (public, typed API)
259
+ src/pycontextfold/bench offline benchmarking platform (adapters, runner, report, gates, CLI)
260
+ crates/
261
+ contextfold-core transformation pipeline (no Tree-sitter)
262
+ contextfold-parsers Tree-sitter integration + format detection
263
+ contextfold-cli standalone CLI
264
+ contextfold-py PyO3 bindings (module: pycontextfold._core)
265
+ tests/ Python + preservation/integration tests
266
+ tests/bench benchmarking platform tests
267
+ crates/*/tests Rust unit/integration tests
268
+ benches/ reproducible benchmarks
269
+ docs/ architecture, profiles, guarantees, limitations
270
+ ```
271
+
272
+ Python is the primary interface; the Rust core exists for speed, memory safety
273
+ and determinism. See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
274
+
275
+ ## Documentation
276
+
277
+ - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)
278
+ - [`docs/PROFILES.md`](docs/PROFILES.md)
279
+ - [`docs/GUARANTEES.md`](docs/GUARANTEES.md)
280
+ - [`docs/LIMITATIONS.md`](docs/LIMITATIONS.md)
281
+ - [`docs/INTEGRATIONS.md`](docs/INTEGRATIONS.md)
282
+ - [`docs/BENCHMARKS.md`](docs/BENCHMARKS.md)
283
+ - [`docs/BENCHMARKING.md`](docs/BENCHMARKING.md)
284
+ - [`docs/ECOSYSTEM_ASSESSMENT.md`](docs/ECOSYSTEM_ASSESSMENT.md)
285
+ - [`docs/ROADMAP.md`](docs/ROADMAP.md)
286
+
287
+ ## License
288
+
289
+ Apache-2.0.
290
+
@@ -0,0 +1,230 @@
1
+ # PyContextFold
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/pycontextfold.svg)](https://pypi.org/project/pycontextfold/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/pycontextfold.svg)](https://pypi.org/project/pycontextfold/)
5
+ [![CI](https://github.com/Mullassery/PyContextFold/actions/workflows/tests.yml/badge.svg)](https://github.com/Mullassery/PyContextFold/actions/workflows/tests.yml)
6
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
7
+
8
+ **A Python library for structure-aware context optimization for LLM consumption.**
9
+
10
+ PyContextFold shrinks source code, technical documentation, logs and structured
11
+ text before you send them to an LLM — cutting token cost while keeping every
12
+ transformation explicit, configurable and reportable. It is a local-first
13
+ `pip install` with a fast, deterministic Rust core underneath, and it never
14
+ sends your content anywhere.
15
+
16
+ It is a *transformation engine for content you already have* — not an inference
17
+ orchestrator, retrieval engine, token-counting library, PDF processor, or agent
18
+ framework.
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pip install pycontextfold
24
+ ```
25
+
26
+ Requires Python ≥ 3.10 (the wheel is built `abi3-py310`). The core, the CLI and
27
+ the offline benchmark need nothing else. Optional ecosystem adapters activate
28
+ when their project is importable; install them individually, e.g.
29
+ `pip install "pycontextfold[tokens]"`, `[pdf]`, `[agents]`, or `[ecosystem]` for
30
+ all of them.
31
+
32
+ ## Quick start
33
+
34
+ ```python
35
+ import pycontextfold as pcf
36
+
37
+ result = pcf.fold(source, profile="safe", path="app.py")
38
+ print(result.output) # transformed text
39
+ print(result.report.detected_language) # "python"
40
+ print(result.report.reduction_ratio) # e.g. 0.19
41
+ print(result.report.syntax_valid) # True
42
+ print(result.report.to_dict()) # serialisable report
43
+ ```
44
+
45
+ Fold a file without ever modifying it:
46
+
47
+ ```python
48
+ result = pcf.fold_file("app.py", profile="code-review")
49
+ open("app.folded.py", "w").write(result.output)
50
+ ```
51
+
52
+ Introspection and full control:
53
+
54
+ ```python
55
+ pcf.profiles() # ['budgeted', 'code-review', ...]
56
+ pcf.profile_info("safe") # {name, description, loss, ...}
57
+ pcf.supported_languages() # [...]
58
+ pcf.detect_language("fn main() {}", path="a.rs") # "rust"
59
+
60
+ # Typed request object with budgets, protection and a caller-supplied tokenizer
61
+ req = pcf.FoldRequest(
62
+ source,
63
+ profile="budgeted",
64
+ language="python",
65
+ budget_bytes=4096,
66
+ protected_ranges=[(0, 128)], # keep the license header
67
+ protected_line_markers=["contextfold:keep"],
68
+ )
69
+ res = pcf.Engine().fold_request(req)
70
+ ```
71
+
72
+ Errors are typed — `UnknownProfileError`, `InvalidConfigError`,
73
+ `BudgetNotMetError` — all subclasses of `ContextFoldError`.
74
+
75
+ ## What it owns (and what it deliberately does not)
76
+
77
+ **Owns:** content-aware parsing, structural transformations, redundancy
78
+ reduction, configurable preservation policies, budget-constrained folding,
79
+ validation of transformed output, and transformation reports.
80
+
81
+ **Does not own** (and never re-implements, imports, or depends on): provider
82
+ tokenization/cost (PyTokenCalc), context discovery/retrieval (PyStreamMCP),
83
+ inference orchestration (PyInferenceManager), PDF extraction (PyStreamPDF),
84
+ vector retrieval evaluation (PyVectorHound), agent tracing (PyAgentHound), or
85
+ OpenAnchor's token-intelligence middleware. Stable *interfaces* are exposed for
86
+ optional integration, but there are no cross-repository dependencies.
87
+
88
+ ## Guarantees
89
+
90
+ - **Deterministic.** The same input, profile, engine version and registry
91
+ produce byte-identical output.
92
+ - **Conservative by default.** The `safe` profile never removes executable
93
+ statements, imports, conditions or error handling.
94
+ - **Honest about loss.** Lossy rules are labelled in the report. The engine
95
+ **never claims semantic equivalence** — only, optionally, syntactic validity.
96
+ - **Local-first.** No network access. Source content is never logged by default.
97
+ - **Non-destructive.** Inputs are never overwritten unless you explicitly opt in.
98
+
99
+ ## Profiles
100
+
101
+ | Profile | Lossy | Summary |
102
+ |------------------|:-----:|---------|
103
+ | `safe` | no | Remove comments, collapse blank-line runs, trim trailing whitespace. Never removes code, imports, conditions or error handling. Docstrings preserved. |
104
+ | `code-review` | no | Preserve implementation logic **and** comments/docstrings; whitespace-only normalisation. |
105
+ | `interface-only` | yes | Keep imports, declarations, signatures and docstrings; replace function bodies with valid placeholders. |
106
+ | `documentation` | yes | Reduce redundant Markdown/prose; preserve headings, code fences, links and normative keywords. |
107
+ | `logs` | yes | Collapse consecutive duplicate log events, retaining configured counts. |
108
+ | `budgeted` | yes | Aggressive deterministic ordering to reach a size/token budget. |
109
+
110
+ Profiles are versioned (`PROFILE_SCHEMA_VERSION`, surfaced in every report).
111
+ See [`docs/PROFILES.md`](docs/PROFILES.md) for per-profile guarantees.
112
+
113
+ ## Token budgets
114
+
115
+ PyContextFold accepts a **target budget** but does not ship a provider
116
+ tokenizer catalogue. Supply a tokenizer implementing `count(text) -> int`, or
117
+ use byte/character budgets (clearly reported as approximations, never as token
118
+ counts):
119
+
120
+ ```python
121
+ class MyTokenizer:
122
+ name = "my-tok"
123
+ def count(self, text: str) -> int:
124
+ ...
125
+
126
+ req = pcf.FoldRequest(source, profile="budgeted",
127
+ budget_tokens=500, tokenizer=MyTokenizer())
128
+ res = pcf.Engine().fold_request(req)
129
+ ```
130
+
131
+ When a budget cannot be met under the configured loss policy, the report
132
+ contains `warnings: ["budget_not_met"]`; the engine does not silently drop code.
133
+
134
+ ## Benchmarking (offline, dependency-light)
135
+
136
+ The `pycontextfold.bench` subpackage measures the trade-offs of folding: how much
137
+ is removed (size/character/token reduction) versus how much matters (ground-truth
138
+ preservation). It runs with nothing else installed and never touches the network.
139
+
140
+ ```python
141
+ from pycontextfold import bench
142
+
143
+ ds = bench.dataset.synthetic_dataset()
144
+ reg = bench.adapters.default_registry()
145
+ adapter = reg.get("contextfold")
146
+
147
+ for doc in ds:
148
+ result = adapter.optimize(bench.OptimizationRequest(doc, method="safe"))
149
+ metrics = bench.metrics.document_metrics(doc, result, ds.ground_truth_for(doc.id))
150
+ print(doc.id, {m.name: round(m.value, 3) for m in metrics if m.available})
151
+ ```
152
+
153
+ Optional ecosystem adapters (PyTokenCalc, PyStreamPDF, PyVectorHound, ...) are
154
+ capability-declared and imported lazily; a requested-but-missing capability is
155
+ reported as *unavailable* rather than silently substituted. See
156
+ [`docs/ROADMAP.md`](docs/ROADMAP.md) and
157
+ [`docs/ECOSYSTEM_ASSESSMENT.md`](docs/ECOSYSTEM_ASSESSMENT.md).
158
+
159
+ Run a whole experiment and emit a single-file HTML report (no CDN, fully
160
+ offline):
161
+
162
+ ```python
163
+ from pycontextfold import bench
164
+
165
+ run = bench.run_experiment(
166
+ bench.dataset.synthetic_dataset(),
167
+ bench.ExperimentConfig(name="demo", dataset="synthetic",
168
+ adapters=("baseline", "contextfold")),
169
+ )
170
+ bench.write_report(run, "report.html") # self-contained, deterministic
171
+ ```
172
+
173
+ There is also a CLI (offline, stdlib only):
174
+
175
+ ```bash
176
+ pycontextfold-bench adapters # list + availability
177
+ pycontextfold-bench run --config exp.json --report report.html
178
+ pycontextfold-bench run --config exp.json --gate # exit 1 on gate failure
179
+ pycontextfold-bench report run.json --out report.html
180
+ ```
181
+
182
+ Gates turn a run into a regression check — an aggregated metric below its
183
+ threshold, or an unavailable metric, fails the run.
184
+
185
+ ## Command-line tool (optional)
186
+
187
+ Installing the package gives you the Python library. The repository also ships a
188
+ standalone `contextfold` CLI and a Rust library for non-Python use:
189
+
190
+ ```bash
191
+ cargo install --path crates/contextfold-cli
192
+ echo 'print("hi") # comment' | contextfold -p safe
193
+ contextfold app.py -p safe --report report.json
194
+ ```
195
+
196
+ ## Architecture
197
+
198
+ ```
199
+ src/pycontextfold Python package (public, typed API)
200
+ src/pycontextfold/bench offline benchmarking platform (adapters, runner, report, gates, CLI)
201
+ crates/
202
+ contextfold-core transformation pipeline (no Tree-sitter)
203
+ contextfold-parsers Tree-sitter integration + format detection
204
+ contextfold-cli standalone CLI
205
+ contextfold-py PyO3 bindings (module: pycontextfold._core)
206
+ tests/ Python + preservation/integration tests
207
+ tests/bench benchmarking platform tests
208
+ crates/*/tests Rust unit/integration tests
209
+ benches/ reproducible benchmarks
210
+ docs/ architecture, profiles, guarantees, limitations
211
+ ```
212
+
213
+ Python is the primary interface; the Rust core exists for speed, memory safety
214
+ and determinism. See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
215
+
216
+ ## Documentation
217
+
218
+ - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)
219
+ - [`docs/PROFILES.md`](docs/PROFILES.md)
220
+ - [`docs/GUARANTEES.md`](docs/GUARANTEES.md)
221
+ - [`docs/LIMITATIONS.md`](docs/LIMITATIONS.md)
222
+ - [`docs/INTEGRATIONS.md`](docs/INTEGRATIONS.md)
223
+ - [`docs/BENCHMARKS.md`](docs/BENCHMARKS.md)
224
+ - [`docs/BENCHMARKING.md`](docs/BENCHMARKING.md)
225
+ - [`docs/ECOSYSTEM_ASSESSMENT.md`](docs/ECOSYSTEM_ASSESSMENT.md)
226
+ - [`docs/ROADMAP.md`](docs/ROADMAP.md)
227
+
228
+ ## License
229
+
230
+ Apache-2.0.
@@ -5,7 +5,7 @@ build-backend = "maturin"
5
5
  [project]
6
6
  name = "pycontextfold"
7
7
  dynamic = ["version"]
8
- description = "Local-first, structure-aware context optimization engine for LLM consumption. Rust core with Tree-sitter parsing, conservative profiles, preservation policies, budget-aware folding and transformation reports."
8
+ description = "Structure-aware context optimization for LLM consumption. Shrink source code, documentation, logs and structured text before sending them to an LLM. Typed Python API with a fast, deterministic Rust core, Tree-sitter parsing, conservative profiles, preservation policies, budget-aware folding and transformation reports."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
 
@@ -33,6 +33,9 @@ classifiers = [
33
33
  ]
34
34
  dependencies = []
35
35
 
36
+ [project.scripts]
37
+ pycontextfold-bench = "pycontextfold.bench.cli:main"
38
+
36
39
  [project.urls]
37
40
  Homepage = "https://github.com/Mullassery/PyContextFold"
38
41
  Documentation = "https://github.com/Mullassery/PyContextFold/blob/main/README.md"
@@ -41,6 +44,29 @@ Issues = "https://github.com/Mullassery/PyContextFold/issues"
41
44
 
42
45
  [project.optional-dependencies]
43
46
  dev = ["pytest>=7.0", "maturin>=1.0", "ruff>=0.5.0"]
47
+ # Optional ecosystem extras: each maps to a sibling project and activates one or
48
+ # more benchmark adapters. All are optional — the core and the offline benchmark
49
+ # run with none of them installed. Install the ones whose projects are reachable
50
+ # from your package index.
51
+ tokens = ["pytokencalc"]
52
+ pdf = ["pystreampdf"]
53
+ agents = ["pyagenthound"]
54
+ api = ["pyapicheck"]
55
+ retrieval = ["pyvectorhound"]
56
+ inference = ["pyinferencemanager", "pystreamai"]
57
+ middleware = ["openanchor"]
58
+ discovery = ["pystreammcp"]
59
+ ecosystem = [
60
+ "pytokencalc",
61
+ "pystreampdf",
62
+ "pyagenthound",
63
+ "pyapicheck",
64
+ "pyvectorhound",
65
+ "pyinferencemanager",
66
+ "pystreamai",
67
+ "openanchor",
68
+ "pystreammcp",
69
+ ]
44
70
 
45
71
  [tool.maturin]
46
72
  python-source = "src"
@@ -50,3 +76,10 @@ include = ["LICENSE"]
50
76
 
51
77
  [tool.pytest.ini_options]
52
78
  testpaths = ["tests"]
79
+ markers = [
80
+ "optional: requires a real (optional) ecosystem package to be installed",
81
+ ]
82
+
83
+ [tool.ruff]
84
+ target-version = "py310"
85
+ extend-exclude = ["tests/fixtures"]