pi-read-chunks 2.0.0 → 2.0.2

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.
package/README.md CHANGED
@@ -19,11 +19,11 @@ Replaces the built-in `read()` for text files via `registerTool`. Images and oth
19
19
 
20
20
  **Context-aware chunk labels** — Chunk labels are line ranges (`start-end`) with char offsets in parentheses, derived by binary-searching the file's `\n` positions. Labels match how the agent reasons (line numbers, not raw char offsets).
21
21
 
22
- **Per-tool-result compression** — The raw chunked-mode payload is JSON; a `tool_result` hook rewrites it into a compact, line-budgeted summary (~25 chunk entries × 120 chars each) before the model sees it, so a multi-chunk scan doesn't blow the context budget. Toggle with `/read-chunks`.
22
+ **Per-tool-result compression** — The raw chunked-mode payload is JSON; a `tool_result` hook rewrites it into a human-readable summary before the model sees it, so a multi-chunk scan doesn't blow the context budget. Toggle with `/read-chunks`.
23
23
 
24
24
  **Optional per-invocation debug dump** — `/read-chunks debug` (toggles on/off) writes each invocation's LLM requests/responses and the final tool return to `/tmp/read-chunks_<YYMMDD-hhmmss>.json`. Disabled by default.
25
25
 
26
- **Summary budget** — `thresholdKB` also acts as a soft cap on total summary size. Each chunk's LLM prompt includes a budget hint: `thresholdKB / numChunks` KB per chunk. For a 160KB file with 50KB threshold (4 chunks), each chunk gets ~12KB of summary budget. A 500KB file (10 chunks) → 5KB per chunk. This keeps the total summary proportional to `thresholdKB` regardless of file size, preventing small files from wasting context while still allowing large files enough room for useful summaries.
26
+ **Summary budget** — By default, summaries preserve names, places, events, dates, and key details without artificial truncation. Typical output stays under ~1.5KB for a 100KB file (~94% reduction from the original). If summaries exceed `thresholdKB`, they are sent back to the LLM for denser compression (up to 3 attempts) without losing factual content.
27
27
 
28
28
 
29
29
  ## Installation
@@ -67,7 +67,7 @@ Copy read-chunks.example.json to:
67
67
 
68
68
  | Key | Default | Notes |
69
69
  | ------------------- | ------- | ---------------------------------------------------------------------------------------------- |
70
- | `thresholdKB` | `50` | Files ≤ this size are returned verbatim; larger files are chunked using this same value as the target chunk size (KB). Also serves as the soft limit for total summary size — the per-chunk summary budget is `thresholdKB / numChunks`, so the total stays bounded to roughly one chunk's worth. |
70
+ | `thresholdKB` | `50` | Files ≤ this size are returned verbatim; larger files are chunked using this same value as the target chunk size (KB). Also acts as the hard cap on total summary size — summaries exceeding this are sent back to the LLM for denser compression without losing factual content. |
71
71
  | `chunkOverlapChars` | `800` | Backward overlap between consecutive chunks, in characters. |
72
72
 
73
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-read-chunks",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "A Pi Agent extension that uses a subagent to enhance `read` functionality for large text files to reduce context bloat, rot and lost in the middle issues.",
5
5
  "keywords": [
6
6
  "pi-package",
package/read-chunks.ts CHANGED
@@ -462,6 +462,45 @@ function formatReadChunksCall(args: any, theme: any, cwd: string): string {
462
462
  return text;
463
463
  }
464
464
 
465
+ // ---------- Summary compression ----------
466
+
467
+ async function compressSummary(
468
+ text: string,
469
+ modelRegistry: any,
470
+ model: any,
471
+ ): Promise<string> {
472
+ if (!model) return text;
473
+ try {
474
+ if (!modelRegistry.hasConfiguredAuth(model)) return text;
475
+ } catch {
476
+ return text;
477
+ }
478
+
479
+ const prompt = `You are compressing a detailed summary into a denser, more concise version. Keep ALL factual content — names, places, events, dates, numbers, key details. Remove filler words, redundant phrases, unnecessary articles, and verbose constructions. Write in a telegraphic, information-dense style. Do not omit or dilute any facts.
480
+
481
+ Compressed summary:
482
+ """
483
+ ${text}
484
+ """
485
+
486
+ Compressed:`;
487
+
488
+ const requestPayload = {
489
+ model: model.id,
490
+ messages: [{ role: "user", content: [{ type: "text", text: prompt }] }],
491
+ };
492
+
493
+ try {
494
+ const response = await modelRegistry.complete(model, requestPayload);
495
+ const contentBlocks = response?.content || [];
496
+ const textBlocks = contentBlocks.filter((c: any) => c.type === "text");
497
+ if (textBlocks.length === 0) return text;
498
+ return textBlocks.map((c: any) => c.text).join(" ").trim();
499
+ } catch {
500
+ return text;
501
+ }
502
+ }
503
+
465
504
  // ---------- Summary builder ----------
466
505
 
467
506
  function buildSummary(parsed: any): string {
@@ -721,6 +760,7 @@ export default function (pi: ExtensionAPI) {
721
760
  mode: query ? "query" : "summary",
722
761
  file: absolutePath,
723
762
  kb_total: totalKB,
763
+ thresholdKB: config.thresholdKB,
724
764
  chunks_scanned: chunks.length,
725
765
  chunks_read: perChunk.length,
726
766
  stop_reason: stopReason,
@@ -774,8 +814,21 @@ export default function (pi: ExtensionAPI) {
774
814
  return;
775
815
  }
776
816
 
817
+ let summaryText = buildSummary(parsed);
818
+
819
+ // Post-compression if summary exceeds thresholdKB
820
+ const model = _ctx.model;
821
+ const maxKB = parsed.thresholdKB;
822
+ if (model && maxKB) {
823
+ const summaryBytes = Buffer.byteLength(summaryText, "utf-8");
824
+ const summaryKB = summaryBytes / 1024;
825
+ if (summaryKB > maxKB) {
826
+ summaryText = await compressSummary(summaryText, _ctx.modelRegistry, model);
827
+ }
828
+ }
829
+
777
830
  return {
778
- content: [{ type: "text", text: buildSummary(parsed) }],
831
+ content: [{ type: "text", text: summaryText }],
779
832
  details: event.details,
780
833
  };
781
834
  });