longloom 1.0.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 (39) hide show
  1. longloom-1.0.0/LICENSE +21 -0
  2. longloom-1.0.0/PKG-INFO +358 -0
  3. longloom-1.0.0/README.md +289 -0
  4. longloom-1.0.0/longloom/__init__.py +87 -0
  5. longloom-1.0.0/longloom/chunks.py +109 -0
  6. longloom-1.0.0/longloom/factory.py +245 -0
  7. longloom-1.0.0/longloom/graph.py +220 -0
  8. longloom-1.0.0/longloom/llm.py +185 -0
  9. longloom-1.0.0/longloom/nvidia.py +166 -0
  10. longloom-1.0.0/longloom/processor.py +114 -0
  11. longloom-1.0.0/longloom/prompts.py +103 -0
  12. longloom-1.0.0/longloom/py.typed +0 -0
  13. longloom-1.0.0/longloom/stores/__init__.py +29 -0
  14. longloom-1.0.0/longloom/stores/base.py +139 -0
  15. longloom-1.0.0/longloom/stores/jsonfile.py +115 -0
  16. longloom-1.0.0/longloom/stores/memory.py +63 -0
  17. longloom-1.0.0/longloom/stores/mongo.py +170 -0
  18. longloom-1.0.0/longloom/stores/naming.py +69 -0
  19. longloom-1.0.0/longloom/stores/sql.py +372 -0
  20. longloom-1.0.0/longloom/stores/sqlite.py +47 -0
  21. longloom-1.0.0/longloom/stores/supabase.py +170 -0
  22. longloom-1.0.0/longloom/tokens.py +67 -0
  23. longloom-1.0.0/longloom.egg-info/PKG-INFO +358 -0
  24. longloom-1.0.0/longloom.egg-info/SOURCES.txt +37 -0
  25. longloom-1.0.0/longloom.egg-info/dependency_links.txt +1 -0
  26. longloom-1.0.0/longloom.egg-info/requires.txt +52 -0
  27. longloom-1.0.0/longloom.egg-info/top_level.txt +1 -0
  28. longloom-1.0.0/pyproject.toml +86 -0
  29. longloom-1.0.0/setup.cfg +4 -0
  30. longloom-1.0.0/tests/test_db_integration.py +204 -0
  31. longloom-1.0.0/tests/test_e2e.py +117 -0
  32. longloom-1.0.0/tests/test_embed_existing.py +272 -0
  33. longloom-1.0.0/tests/test_factory.py +126 -0
  34. longloom-1.0.0/tests/test_llm_adapter.py +52 -0
  35. longloom-1.0.0/tests/test_production_hardening.py +145 -0
  36. longloom-1.0.0/tests/test_real_nvidia.py +86 -0
  37. longloom-1.0.0/tests/test_real_openai.py +71 -0
  38. longloom-1.0.0/tests/test_stores.py +78 -0
  39. longloom-1.0.0/tests/test_unstructured_no_loss.py +118 -0
longloom-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LongLoom Maintainers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,358 @@
1
+ Metadata-Version: 2.4
2
+ Name: longloom
3
+ Version: 1.0.0
4
+ Summary: Weave documents of any length through LLM map/merge/repair — any LLM, any storage, measured no-loss
5
+ Author: LongLoom Maintainers
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/sumitnemade/longloom
8
+ Project-URL: Documentation, https://github.com/sumitnemade/longloom#readme
9
+ Project-URL: Changelog, https://github.com/sumitnemade/longloom/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/sumitnemade/longloom/issues
11
+ Keywords: llm,long-context,map-reduce,langgraph,chunking,rag,summarization,nvidia,openai,postgresql,mongodb
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: Text Processing :: General
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: langgraph>=0.2.0
28
+ Requires-Dist: langchain-core>=0.3.0
29
+ Provides-Extra: tiktoken
30
+ Requires-Dist: tiktoken>=0.7.0; extra == "tiktoken"
31
+ Provides-Extra: nvidia
32
+ Requires-Dist: requests>=2.31; extra == "nvidia"
33
+ Provides-Extra: openai
34
+ Requires-Dist: langchain-openai>=0.2.0; extra == "openai"
35
+ Provides-Extra: sql
36
+ Requires-Dist: sqlalchemy>=2.0; extra == "sql"
37
+ Provides-Extra: postgres
38
+ Requires-Dist: sqlalchemy>=2.0; extra == "postgres"
39
+ Requires-Dist: psycopg2-binary>=2.9; extra == "postgres"
40
+ Provides-Extra: mysql
41
+ Requires-Dist: sqlalchemy>=2.0; extra == "mysql"
42
+ Requires-Dist: pymysql>=1.1; extra == "mysql"
43
+ Provides-Extra: mongodb
44
+ Requires-Dist: pymongo>=4.6; extra == "mongodb"
45
+ Provides-Extra: supabase
46
+ Requires-Dist: supabase>=2.4; extra == "supabase"
47
+ Provides-Extra: databases
48
+ Requires-Dist: sqlalchemy>=2.0; extra == "databases"
49
+ Requires-Dist: psycopg2-binary>=2.9; extra == "databases"
50
+ Requires-Dist: pymysql>=1.1; extra == "databases"
51
+ Requires-Dist: pymongo>=4.6; extra == "databases"
52
+ Requires-Dist: supabase>=2.4; extra == "databases"
53
+ Provides-Extra: all
54
+ Requires-Dist: tiktoken>=0.7.0; extra == "all"
55
+ Requires-Dist: requests>=2.31; extra == "all"
56
+ Requires-Dist: langchain-openai>=0.2.0; extra == "all"
57
+ Requires-Dist: sqlalchemy>=2.0; extra == "all"
58
+ Requires-Dist: psycopg2-binary>=2.9; extra == "all"
59
+ Requires-Dist: pymysql>=1.1; extra == "all"
60
+ Requires-Dist: pymongo>=4.6; extra == "all"
61
+ Provides-Extra: dev
62
+ Requires-Dist: pytest>=8.0; extra == "dev"
63
+ Requires-Dist: tiktoken>=0.7.0; extra == "dev"
64
+ Requires-Dist: pymongo>=4.6; extra == "dev"
65
+ Requires-Dist: sqlalchemy>=2.0; extra == "dev"
66
+ Requires-Dist: psycopg2-binary>=2.9; extra == "dev"
67
+ Requires-Dist: pymysql>=1.1; extra == "dev"
68
+ Dynamic: license-file
69
+
70
+ <div align="center">
71
+
72
+ # 🧶 LongLoom
73
+
74
+ **Weave documents of any length through LLM map/merge/repair — without losing a thread.**
75
+
76
+ [![PyPI](https://img.shields.io/pypi/v/longloom)](https://pypi.org/project/longloom/)
77
+ [![Python](https://img.shields.io/pypi/pyversions/longloom)](https://pypi.org/project/longloom/)
78
+ [![CI](https://github.com/sumitnemade/longloom/actions/workflows/ci.yml/badge.svg)](https://github.com/sumitnemade/longloom/actions/workflows/ci.yml)
79
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
80
+ [![Typed](https://img.shields.io/badge/typing-typed-blue)](longloom/py.typed)
81
+ [![Tests](https://img.shields.io/badge/tests-50%20passing-brightgreen)](#test-it-yourself)
82
+
83
+ *Any LLM · Any storage · Measured no-loss · 2 core dependencies*
84
+
85
+ </div>
86
+
87
+ ---
88
+
89
+ Your model accepts 8K, 128K, or 200K tokens — your documents don't care.
90
+ Annual reports, BRDs, legal contracts, log archives, codebases: they all blow
91
+ past the window, and naive truncation silently drops the facts you need.
92
+
93
+ **LongLoom** splits the problem the way humans do — read in chunks, extract,
94
+ combine, condense — as a LangGraph pipeline with **measured** information
95
+ preservation, over **any LLM** you already use and **any storage** you already run.
96
+
97
+ ```text
98
+ ┌───────────────┐
99
+ big document ─► CHUNK + STORE ─► every byte persisted (SQLite / Postgres / Mongo / …)
100
+ └───────┬───────┘
101
+ ▼ MAP one LLM call per chunk — verbatim or summarizing
102
+ ┌───────────────┐
103
+ │ map results │
104
+ └───────┬───────┘
105
+ ▼ MERGE window-sized hierarchical combine calls
106
+ ┌───────────────┐ (scales past any single context window)
107
+ │ merged draft │
108
+ └───────┬───────┘
109
+ ▼ REPAIR bounded condensation loop, only if still too big
110
+ ┌───────────────┐
111
+ │ final result │ + verification: every fact accounted for
112
+ └───────────────┘
113
+ ```
114
+
115
+ ## Install
116
+
117
+ ```bash
118
+ pip install "longloom[all]" # batteries included
119
+ ```
120
+
121
+ or just the core (two dependencies: `langgraph` + `langchain-core`):
122
+
123
+ ```bash
124
+ pip install longloom
125
+ ```
126
+
127
+ Python ≥ 3.10 · Optional extras: `[tiktoken]` `[nvidia]` `[openai]` `[postgres]` `[mysql]` `[mongodb]` `[supabase]` `[databases]`
128
+
129
+ ## 60-second quickstart
130
+
131
+ ```python
132
+ import os
133
+ from longloom import MapReduceProcessor, SQLiteContextStore, init_llm
134
+
135
+ processor = MapReduceProcessor(
136
+ llm=init_llm("nvidia", "nvidia/nemotron-3-ultra-550b-a55b",
137
+ api_key=os.environ["NVIDIA_API_KEY"], # your app owns the key
138
+ thinking=False),
139
+ store=SQLiteContextStore("ctx.db"),
140
+ max_chunk_tokens=3000,
141
+ max_context_tokens=8192, # your model's real window
142
+ )
143
+
144
+ result = processor.process(
145
+ annual_report, # 50K tokens? 500K? doesn't matter
146
+ task="Summarize the key findings",
147
+ )
148
+ print(result["final_result"])
149
+ print(f"{result['num_chunks']} chunks · {result['total_input_tokens']:,} tokens · "
150
+ f"{result['repair_attempts']} repairs")
151
+ ```
152
+
153
+ Every run is **persisted and queryable** afterwards:
154
+
155
+ ```python
156
+ processor.search_session(result["session_id"], "revenue") # which chunks mention it
157
+ processor.retrieve_chunks(result["session_id"]) # original text, byte-faithful
158
+ processor.get_session_info(result["session_id"]) # chunk/token stats
159
+ ```
160
+
161
+ ## When facts must not be lost
162
+
163
+ Summarizing prompts measurably drop facts (we measured 2–10% on messy
164
+ documents — different facts every run). When you need *everything*:
165
+
166
+ ```python
167
+ from longloom import PromptTemplates
168
+
169
+ processor = MapReduceProcessor(
170
+ llm=llm,
171
+ store=store,
172
+ prompts=PromptTemplates.for_extraction(), # verbatim line-copy mode
173
+ ...
174
+ )
175
+ ```
176
+
177
+ **Measured result: 100% fact preservation (41/41 planted facts in a log/JSON/
178
+ email/table/document mix — stable across repeated real-LLM runs on NVIDIA and
179
+ OpenAI).** The trade-off is output length: extraction copies, it doesn't
180
+ compress. Summaries → `DEFAULT_PROMPTS`; completeness → `for_extraction()`.
181
+
182
+ ## Any LLM
183
+
184
+ ```python
185
+ init_llm("nvidia", "nvidia/nemotron-3-ultra-550b-a55b",
186
+ api_key=os.environ["NVIDIA_API_KEY"], thinking=False)
187
+ init_llm("openai", "gpt-4o-mini", api_key=os.environ["OPENAI_API_KEY"])
188
+ init_llm("anthropic", "claude-sonnet-4-20250514", api_key=os.environ["ANTHROPIC_API_KEY"])
189
+ init_llm("groq" | "mistral" | "deepseek" | "together", ..., api_key=...)
190
+ init_llm("ollama", "llama3.1") # no key needed
191
+ ```
192
+
193
+ > 🔐 **Keys are constructor arguments, never environment reads.** LongLoom
194
+ > itself never touches `os.environ` for credentials — your application reads
195
+ > the secret (vault, env, wherever) and passes it explicitly. Explicit beats
196
+ > implicit when credentials are involved.
197
+
198
+ …or bring your own: any LangChain `BaseChatModel`, or a bare function —
199
+ the minimum contract is `str → str`:
200
+
201
+ ```python
202
+ processor = MapReduceProcessor(llm=lambda prompt: my_gateway(prompt), store=store)
203
+ ```
204
+
205
+ Provider quirks are handled for you: NVIDIA reasoning models get real model
206
+ IDs, a `thinking` toggle, and a `content=None` starvation guard; OpenAI
207
+ `extra_body` and temperature restrictions are abstracted away.
208
+
209
+ ## Any storage
210
+
211
+ ```python
212
+ from longloom import create_store
213
+
214
+ store = create_store("sqlite:///ctx.db") # default, single file
215
+ store = create_store("postgresql://user:pw@host/longloom")
216
+ store = create_store("mysql://user:pw@host/longloom")
217
+ store = create_store("mongodb://host:27017/longloom")
218
+ store = create_store("https://myteam.supabase.co") # + SUPABASE_KEY
219
+ store = create_store("json://data") # human-readable JSONL
220
+ store = create_store("memory://") # ephemeral
221
+ ```
222
+
223
+ | Backend | Extra | Notes |
224
+ |---|---|---|
225
+ | In-memory | — | zero-dependency default |
226
+ | SQLite | — (stdlib) | inline content or file-backed |
227
+ | JSONL files | — | one readable file per session |
228
+ | **PostgreSQL** | `[postgres]` | SQLAlchemy or psycopg2/psycopg |
229
+ | **MySQL / MariaDB** | `[mysql]` | SQLAlchemy or PyMySQL |
230
+ | **MongoDB** | `[mongodb]` | server-side regex search, `$group` stats |
231
+ | **Supabase** | `[supabase]` | REST via supabase-py — no DB connection needed |
232
+ | **Your own** | — | subclass `BaseContextStore` — implement four methods |
233
+
234
+ All backends pass the identical contract suite (round-trip, filters, budget,
235
+ upsert idempotency, search, stats, delete).
236
+
237
+ ## Embedding in existing databases
238
+
239
+ No dedicated database needed — LongLoom lives inside **your** tables with
240
+ **your** column names, next to your application's own data:
241
+
242
+ ```python
243
+ store = create_store(
244
+ "postgresql://app@host/mydb", # the database you already run
245
+ table="app_document_chunks", # your table
246
+ columns={ # your column names
247
+ "id": "chunk_pk", "content": "text_body",
248
+ "metadata": "meta_json", "session_id": "sess",
249
+ },
250
+ namespace="support-agent", # several apps share one table safely
251
+ init_schema=False, # your migrations own the DDL
252
+ )
253
+ ```
254
+
255
+ | Option | Meaning | Supported by |
256
+ |---|---|---|
257
+ | `table=` | your table/collection name | SQL, MongoDB, Supabase |
258
+ | `columns=` | logical → physical field mapping | SQL, MongoDB, Supabase |
259
+ | `namespace=` + `namespace_field=` | discriminator so multiple apps share one table | SQL, MongoDB, Supabase |
260
+ | `init_schema=False` | your migrations own the DDL | SQL backends |
261
+ | `SqlContextStore.ddl()` / `table_sql(...)` | generate DDL matching your names, for your migration tooling | SQL, Supabase |
262
+
263
+ Same table, two apps, zero cross-talk — namespaces isolate sessions even with
264
+ identical session IDs. See `examples/17_embed_existing_database.py`.
265
+
266
+ ## Built for production
267
+
268
+ | | |
269
+ |---|---|
270
+ | **Hierarchical merge** | Verbatim runs beyond one window still complete — merge calls are grouped and combined recursively (verified: a 45K-token extraction survives an 8K window). |
271
+ | **Retry + pacing** | Exponential backoff for 429/5xx/timeouts, optional min-interval between calls for long matrix runs. |
272
+ | **Bounded repair** | Oversized results condense with a capped attempt count — no infinite loops, no silent truncation. |
273
+ | **Token-count guardrails** | tiktoken exact or zero-dep estimator; chunk sizes clamped so map results are always combinable. |
274
+ | **Typed & versioned** | `py.typed`, semantic versioning, CHANGELOG, per-provider extras. |
275
+
276
+ ### Honest limits
277
+
278
+ - Map/merge **aggregates**; it doesn't reason globally — cross-chunk conflicts
279
+ (two versions of the same figure) are preserved side by side, not reconciled.
280
+ - Final results don't cite source chunks (the store does — provenance output is
281
+ on the roadmap).
282
+ - The map phase is sequential; parallelize externally for very large corpora.
283
+ - tiktoken approximates non-OpenAI tokenizers — keep 2× headroom on chunk sizes.
284
+
285
+ ## Examples — every claim is a runnable script
286
+
287
+ ```bash
288
+ git clone https://github.com/sumitnemade/longloom && cd longloom
289
+ pip install -e ".[dev]"
290
+ export NVIDIA_API_KEY=... # or OPENAI_API_KEY=...
291
+ python examples/run_all.py # 17 examples; skips gracefully what's missing
292
+ ```
293
+
294
+ | # | Example | One-liner |
295
+ |---|---|---|
296
+ | 01 | quickstart | minimal pipeline, zero setup |
297
+ | 02 | all storage backends | every store via one URL |
298
+ | 03–06 | NVIDIA · OpenAI · Anthropic · local Ollama | same workflow, four vendors |
299
+ | 07 | custom callable | a bare function as the LLM (routing pattern) |
300
+ | 08–11 | PostgreSQL · MySQL · MongoDB · Supabase | one example per database |
301
+ | 12 | custom backend | a working Redis store in ~40 lines |
302
+ | 13 | advanced | custom prompts, token counters, brace-safety |
303
+ | 14 | **NVIDIA × every database** | real Nemotron over 8 backends |
304
+ | 15 | **OpenAI × every database** | real GPT over 8 backends |
305
+ | 16 | **no-loss extraction** | 41/41 facts on a hostile document mix |
306
+ | 17 | **embed in existing database** | your table, your columns, namespaces |
307
+ | 18 | **enterprise gateway** | corporate LLM gateway + existing DB + tenant namespace |
308
+
309
+ ## Test it yourself
310
+
311
+ ```bash
312
+ pip install -e ".[dev]"
313
+ pytest tests/ -v # 50 tests; network-dependent ones self-skip
314
+ NVIDIA_API_KEY=... pytest tests/ -v # + real NVIDIA, Postgres, MySQL, MongoDB
315
+ OPENAI_API_KEY=... pytest tests/ -v # + real OpenAI end-to-end
316
+ ```
317
+
318
+ ## FAQ
319
+
320
+ **Do I need LangChain?** Only `langchain-core` (a lightweight dependency).
321
+ Your LLM can even be a plain function.
322
+
323
+ **What if my output is bigger than the window?** The repair loop condenses it,
324
+ and the merge stage itself groups results hierarchically — oversized inputs are
325
+ the library's job, not yours.
326
+
327
+ **Is my data sent anywhere?** Only to the LLM provider you configure. Storage
328
+ stays in whatever database *you* chose.
329
+
330
+ **Why not just use a 200K-token model?** Cost, latency, and attention decay.
331
+ Chunked extraction is cheaper per token and verifiably complete — and LongLoom
332
+ works with *any* window, so you're never locked to a model generation.
333
+
334
+ ## Releasing (maintainers)
335
+
336
+ ```bash
337
+ scripts/release.sh 1.2.0 --push # tests → bump → changelog → tag → CI publishes to PyPI
338
+ ```
339
+
340
+ One-time setup:
341
+ 1. Create the project on pypi.org, then **Trusted Publishing**: owner =
342
+ your GitHub username/org, repo = `longloom`, workflow = `release.yml`,
343
+ environment = `pypi` — no API tokens live in the repository.
344
+ 2. Push the tag (`--push`) — the release workflow builds, checks metadata,
345
+ verifies tag↔version, publishes, and attaches artifacts to a GitHub release.
346
+ 3. Optional local path: `scripts/release.sh 1.2.0 --publish` with
347
+ `PYPI_TOKEN` set (TestPyPI: `--testpypi` with `TESTPYPI_TOKEN`).
348
+
349
+ ## Contributing
350
+
351
+ Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for setup and
352
+ conventions (every fix ships with a test; honest verification is the house
353
+ style). By participating you agree to the
354
+ [Code of Conduct](CODE_OF_CONDUCT.md).
355
+
356
+ ## License
357
+
358
+ [MIT](LICENSE) — © The LongLoom Maintainers
@@ -0,0 +1,289 @@
1
+ <div align="center">
2
+
3
+ # 🧶 LongLoom
4
+
5
+ **Weave documents of any length through LLM map/merge/repair — without losing a thread.**
6
+
7
+ [![PyPI](https://img.shields.io/pypi/v/longloom)](https://pypi.org/project/longloom/)
8
+ [![Python](https://img.shields.io/pypi/pyversions/longloom)](https://pypi.org/project/longloom/)
9
+ [![CI](https://github.com/sumitnemade/longloom/actions/workflows/ci.yml/badge.svg)](https://github.com/sumitnemade/longloom/actions/workflows/ci.yml)
10
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
11
+ [![Typed](https://img.shields.io/badge/typing-typed-blue)](longloom/py.typed)
12
+ [![Tests](https://img.shields.io/badge/tests-50%20passing-brightgreen)](#test-it-yourself)
13
+
14
+ *Any LLM · Any storage · Measured no-loss · 2 core dependencies*
15
+
16
+ </div>
17
+
18
+ ---
19
+
20
+ Your model accepts 8K, 128K, or 200K tokens — your documents don't care.
21
+ Annual reports, BRDs, legal contracts, log archives, codebases: they all blow
22
+ past the window, and naive truncation silently drops the facts you need.
23
+
24
+ **LongLoom** splits the problem the way humans do — read in chunks, extract,
25
+ combine, condense — as a LangGraph pipeline with **measured** information
26
+ preservation, over **any LLM** you already use and **any storage** you already run.
27
+
28
+ ```text
29
+ ┌───────────────┐
30
+ big document ─► CHUNK + STORE ─► every byte persisted (SQLite / Postgres / Mongo / …)
31
+ └───────┬───────┘
32
+ ▼ MAP one LLM call per chunk — verbatim or summarizing
33
+ ┌───────────────┐
34
+ │ map results │
35
+ └───────┬───────┘
36
+ ▼ MERGE window-sized hierarchical combine calls
37
+ ┌───────────────┐ (scales past any single context window)
38
+ │ merged draft │
39
+ └───────┬───────┘
40
+ ▼ REPAIR bounded condensation loop, only if still too big
41
+ ┌───────────────┐
42
+ │ final result │ + verification: every fact accounted for
43
+ └───────────────┘
44
+ ```
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ pip install "longloom[all]" # batteries included
50
+ ```
51
+
52
+ or just the core (two dependencies: `langgraph` + `langchain-core`):
53
+
54
+ ```bash
55
+ pip install longloom
56
+ ```
57
+
58
+ Python ≥ 3.10 · Optional extras: `[tiktoken]` `[nvidia]` `[openai]` `[postgres]` `[mysql]` `[mongodb]` `[supabase]` `[databases]`
59
+
60
+ ## 60-second quickstart
61
+
62
+ ```python
63
+ import os
64
+ from longloom import MapReduceProcessor, SQLiteContextStore, init_llm
65
+
66
+ processor = MapReduceProcessor(
67
+ llm=init_llm("nvidia", "nvidia/nemotron-3-ultra-550b-a55b",
68
+ api_key=os.environ["NVIDIA_API_KEY"], # your app owns the key
69
+ thinking=False),
70
+ store=SQLiteContextStore("ctx.db"),
71
+ max_chunk_tokens=3000,
72
+ max_context_tokens=8192, # your model's real window
73
+ )
74
+
75
+ result = processor.process(
76
+ annual_report, # 50K tokens? 500K? doesn't matter
77
+ task="Summarize the key findings",
78
+ )
79
+ print(result["final_result"])
80
+ print(f"{result['num_chunks']} chunks · {result['total_input_tokens']:,} tokens · "
81
+ f"{result['repair_attempts']} repairs")
82
+ ```
83
+
84
+ Every run is **persisted and queryable** afterwards:
85
+
86
+ ```python
87
+ processor.search_session(result["session_id"], "revenue") # which chunks mention it
88
+ processor.retrieve_chunks(result["session_id"]) # original text, byte-faithful
89
+ processor.get_session_info(result["session_id"]) # chunk/token stats
90
+ ```
91
+
92
+ ## When facts must not be lost
93
+
94
+ Summarizing prompts measurably drop facts (we measured 2–10% on messy
95
+ documents — different facts every run). When you need *everything*:
96
+
97
+ ```python
98
+ from longloom import PromptTemplates
99
+
100
+ processor = MapReduceProcessor(
101
+ llm=llm,
102
+ store=store,
103
+ prompts=PromptTemplates.for_extraction(), # verbatim line-copy mode
104
+ ...
105
+ )
106
+ ```
107
+
108
+ **Measured result: 100% fact preservation (41/41 planted facts in a log/JSON/
109
+ email/table/document mix — stable across repeated real-LLM runs on NVIDIA and
110
+ OpenAI).** The trade-off is output length: extraction copies, it doesn't
111
+ compress. Summaries → `DEFAULT_PROMPTS`; completeness → `for_extraction()`.
112
+
113
+ ## Any LLM
114
+
115
+ ```python
116
+ init_llm("nvidia", "nvidia/nemotron-3-ultra-550b-a55b",
117
+ api_key=os.environ["NVIDIA_API_KEY"], thinking=False)
118
+ init_llm("openai", "gpt-4o-mini", api_key=os.environ["OPENAI_API_KEY"])
119
+ init_llm("anthropic", "claude-sonnet-4-20250514", api_key=os.environ["ANTHROPIC_API_KEY"])
120
+ init_llm("groq" | "mistral" | "deepseek" | "together", ..., api_key=...)
121
+ init_llm("ollama", "llama3.1") # no key needed
122
+ ```
123
+
124
+ > 🔐 **Keys are constructor arguments, never environment reads.** LongLoom
125
+ > itself never touches `os.environ` for credentials — your application reads
126
+ > the secret (vault, env, wherever) and passes it explicitly. Explicit beats
127
+ > implicit when credentials are involved.
128
+
129
+ …or bring your own: any LangChain `BaseChatModel`, or a bare function —
130
+ the minimum contract is `str → str`:
131
+
132
+ ```python
133
+ processor = MapReduceProcessor(llm=lambda prompt: my_gateway(prompt), store=store)
134
+ ```
135
+
136
+ Provider quirks are handled for you: NVIDIA reasoning models get real model
137
+ IDs, a `thinking` toggle, and a `content=None` starvation guard; OpenAI
138
+ `extra_body` and temperature restrictions are abstracted away.
139
+
140
+ ## Any storage
141
+
142
+ ```python
143
+ from longloom import create_store
144
+
145
+ store = create_store("sqlite:///ctx.db") # default, single file
146
+ store = create_store("postgresql://user:pw@host/longloom")
147
+ store = create_store("mysql://user:pw@host/longloom")
148
+ store = create_store("mongodb://host:27017/longloom")
149
+ store = create_store("https://myteam.supabase.co") # + SUPABASE_KEY
150
+ store = create_store("json://data") # human-readable JSONL
151
+ store = create_store("memory://") # ephemeral
152
+ ```
153
+
154
+ | Backend | Extra | Notes |
155
+ |---|---|---|
156
+ | In-memory | — | zero-dependency default |
157
+ | SQLite | — (stdlib) | inline content or file-backed |
158
+ | JSONL files | — | one readable file per session |
159
+ | **PostgreSQL** | `[postgres]` | SQLAlchemy or psycopg2/psycopg |
160
+ | **MySQL / MariaDB** | `[mysql]` | SQLAlchemy or PyMySQL |
161
+ | **MongoDB** | `[mongodb]` | server-side regex search, `$group` stats |
162
+ | **Supabase** | `[supabase]` | REST via supabase-py — no DB connection needed |
163
+ | **Your own** | — | subclass `BaseContextStore` — implement four methods |
164
+
165
+ All backends pass the identical contract suite (round-trip, filters, budget,
166
+ upsert idempotency, search, stats, delete).
167
+
168
+ ## Embedding in existing databases
169
+
170
+ No dedicated database needed — LongLoom lives inside **your** tables with
171
+ **your** column names, next to your application's own data:
172
+
173
+ ```python
174
+ store = create_store(
175
+ "postgresql://app@host/mydb", # the database you already run
176
+ table="app_document_chunks", # your table
177
+ columns={ # your column names
178
+ "id": "chunk_pk", "content": "text_body",
179
+ "metadata": "meta_json", "session_id": "sess",
180
+ },
181
+ namespace="support-agent", # several apps share one table safely
182
+ init_schema=False, # your migrations own the DDL
183
+ )
184
+ ```
185
+
186
+ | Option | Meaning | Supported by |
187
+ |---|---|---|
188
+ | `table=` | your table/collection name | SQL, MongoDB, Supabase |
189
+ | `columns=` | logical → physical field mapping | SQL, MongoDB, Supabase |
190
+ | `namespace=` + `namespace_field=` | discriminator so multiple apps share one table | SQL, MongoDB, Supabase |
191
+ | `init_schema=False` | your migrations own the DDL | SQL backends |
192
+ | `SqlContextStore.ddl()` / `table_sql(...)` | generate DDL matching your names, for your migration tooling | SQL, Supabase |
193
+
194
+ Same table, two apps, zero cross-talk — namespaces isolate sessions even with
195
+ identical session IDs. See `examples/17_embed_existing_database.py`.
196
+
197
+ ## Built for production
198
+
199
+ | | |
200
+ |---|---|
201
+ | **Hierarchical merge** | Verbatim runs beyond one window still complete — merge calls are grouped and combined recursively (verified: a 45K-token extraction survives an 8K window). |
202
+ | **Retry + pacing** | Exponential backoff for 429/5xx/timeouts, optional min-interval between calls for long matrix runs. |
203
+ | **Bounded repair** | Oversized results condense with a capped attempt count — no infinite loops, no silent truncation. |
204
+ | **Token-count guardrails** | tiktoken exact or zero-dep estimator; chunk sizes clamped so map results are always combinable. |
205
+ | **Typed & versioned** | `py.typed`, semantic versioning, CHANGELOG, per-provider extras. |
206
+
207
+ ### Honest limits
208
+
209
+ - Map/merge **aggregates**; it doesn't reason globally — cross-chunk conflicts
210
+ (two versions of the same figure) are preserved side by side, not reconciled.
211
+ - Final results don't cite source chunks (the store does — provenance output is
212
+ on the roadmap).
213
+ - The map phase is sequential; parallelize externally for very large corpora.
214
+ - tiktoken approximates non-OpenAI tokenizers — keep 2× headroom on chunk sizes.
215
+
216
+ ## Examples — every claim is a runnable script
217
+
218
+ ```bash
219
+ git clone https://github.com/sumitnemade/longloom && cd longloom
220
+ pip install -e ".[dev]"
221
+ export NVIDIA_API_KEY=... # or OPENAI_API_KEY=...
222
+ python examples/run_all.py # 17 examples; skips gracefully what's missing
223
+ ```
224
+
225
+ | # | Example | One-liner |
226
+ |---|---|---|
227
+ | 01 | quickstart | minimal pipeline, zero setup |
228
+ | 02 | all storage backends | every store via one URL |
229
+ | 03–06 | NVIDIA · OpenAI · Anthropic · local Ollama | same workflow, four vendors |
230
+ | 07 | custom callable | a bare function as the LLM (routing pattern) |
231
+ | 08–11 | PostgreSQL · MySQL · MongoDB · Supabase | one example per database |
232
+ | 12 | custom backend | a working Redis store in ~40 lines |
233
+ | 13 | advanced | custom prompts, token counters, brace-safety |
234
+ | 14 | **NVIDIA × every database** | real Nemotron over 8 backends |
235
+ | 15 | **OpenAI × every database** | real GPT over 8 backends |
236
+ | 16 | **no-loss extraction** | 41/41 facts on a hostile document mix |
237
+ | 17 | **embed in existing database** | your table, your columns, namespaces |
238
+ | 18 | **enterprise gateway** | corporate LLM gateway + existing DB + tenant namespace |
239
+
240
+ ## Test it yourself
241
+
242
+ ```bash
243
+ pip install -e ".[dev]"
244
+ pytest tests/ -v # 50 tests; network-dependent ones self-skip
245
+ NVIDIA_API_KEY=... pytest tests/ -v # + real NVIDIA, Postgres, MySQL, MongoDB
246
+ OPENAI_API_KEY=... pytest tests/ -v # + real OpenAI end-to-end
247
+ ```
248
+
249
+ ## FAQ
250
+
251
+ **Do I need LangChain?** Only `langchain-core` (a lightweight dependency).
252
+ Your LLM can even be a plain function.
253
+
254
+ **What if my output is bigger than the window?** The repair loop condenses it,
255
+ and the merge stage itself groups results hierarchically — oversized inputs are
256
+ the library's job, not yours.
257
+
258
+ **Is my data sent anywhere?** Only to the LLM provider you configure. Storage
259
+ stays in whatever database *you* chose.
260
+
261
+ **Why not just use a 200K-token model?** Cost, latency, and attention decay.
262
+ Chunked extraction is cheaper per token and verifiably complete — and LongLoom
263
+ works with *any* window, so you're never locked to a model generation.
264
+
265
+ ## Releasing (maintainers)
266
+
267
+ ```bash
268
+ scripts/release.sh 1.2.0 --push # tests → bump → changelog → tag → CI publishes to PyPI
269
+ ```
270
+
271
+ One-time setup:
272
+ 1. Create the project on pypi.org, then **Trusted Publishing**: owner =
273
+ your GitHub username/org, repo = `longloom`, workflow = `release.yml`,
274
+ environment = `pypi` — no API tokens live in the repository.
275
+ 2. Push the tag (`--push`) — the release workflow builds, checks metadata,
276
+ verifies tag↔version, publishes, and attaches artifacts to a GitHub release.
277
+ 3. Optional local path: `scripts/release.sh 1.2.0 --publish` with
278
+ `PYPI_TOKEN` set (TestPyPI: `--testpypi` with `TESTPYPI_TOKEN`).
279
+
280
+ ## Contributing
281
+
282
+ Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for setup and
283
+ conventions (every fix ships with a test; honest verification is the house
284
+ style). By participating you agree to the
285
+ [Code of Conduct](CODE_OF_CONDUCT.md).
286
+
287
+ ## License
288
+
289
+ [MIT](LICENSE) — © The LongLoom Maintainers