recursive-llm-ts 4.5.0 → 4.7.0

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
@@ -14,6 +14,7 @@ TypeScript/JavaScript package for [Recursive Language Models (RLM)](https://gith
14
14
 
15
15
  **Performance & Resilience**
16
16
  - **Pure Go Backend** - 50x faster startup, 3x less memory vs Python
17
+ - **Context Overflow Recovery** - Automatic detection and 6 reduction strategies (mapreduce, truncate, chunked, tfidf, textrank, refine)
17
18
  - **Caching** - Exact-match caching with in-memory and file-based backends
18
19
  - **Retry & Fallback** - Exponential backoff, jitter, and multi-provider fallback chains
19
20
  - **AbortController** - Cancel any operation mid-flight
@@ -31,7 +32,7 @@ TypeScript/JavaScript package for [Recursive Language Models (RLM)](https://gith
31
32
  - **Meta-Agent Mode** - Automatically optimize queries for better results
32
33
  - **Observability** - OpenTelemetry tracing, Langfuse integration, and debug logging
33
34
  - **File Storage** - Process local directories or S3/MinIO/LocalStack buckets as LLM context
34
- - **120+ Tests** - Comprehensive Vitest test suite
35
+ - **150+ Tests** - Comprehensive Vitest + Go test suites
35
36
 
36
37
  ## Installation
37
38
 
@@ -281,12 +282,18 @@ const results = await rlm.batchCompletion([
281
282
  Rich error hierarchy with actionable information:
282
283
 
283
284
  ```typescript
284
- import { RLMRateLimitError, RLMValidationError, RLMTimeoutError } from 'recursive-llm-ts';
285
+ import {
286
+ RLMRateLimitError, RLMValidationError,
287
+ RLMTimeoutError, RLMContextOverflowError
288
+ } from 'recursive-llm-ts';
285
289
 
286
290
  try {
287
291
  const result = await rlm.completion(query, context);
288
292
  } catch (err) {
289
- if (err instanceof RLMRateLimitError) {
293
+ if (err instanceof RLMContextOverflowError) {
294
+ console.log(`Context overflow: ${err.requestTokens} tokens > ${err.modelLimit} limit`);
295
+ // Enable context_overflow config to auto-recover from this
296
+ } else if (err instanceof RLMRateLimitError) {
290
297
  console.log(`Rate limited. Retry after: ${err.retryAfter}s`);
291
298
  } else if (err instanceof RLMValidationError) {
292
299
  console.log(`Schema mismatch:`, err.zodErrors);
@@ -297,6 +304,57 @@ try {
297
304
  }
298
305
  ```
299
306
 
307
+ ### Context Overflow Handling
308
+
309
+ Automatically detect and recover from context window overflows. When your input exceeds the model's token limit, RLM catches the error and applies a reduction strategy to fit the context within bounds.
310
+
311
+ ```typescript
312
+ const rlm = new RLM('gpt-4o-mini', {
313
+ api_key: process.env.OPENAI_API_KEY,
314
+ context_overflow: {
315
+ enabled: true, // Enable overflow recovery (default: true)
316
+ strategy: 'tfidf', // Reduction strategy (see table below)
317
+ max_model_tokens: 32768, // Override auto-detected limit (optional)
318
+ safety_margin: 0.15, // Reserve 15% for prompts/overhead (default: 0.15)
319
+ max_reduction_attempts: 3, // Max retry attempts (default: 3)
320
+ }
321
+ });
322
+
323
+ // Process a document that may exceed the model's context window
324
+ const result = await rlm.completion(
325
+ 'Summarize the key findings',
326
+ veryLargeDocument // If too large, auto-reduces and retries
327
+ );
328
+ ```
329
+
330
+ **Builder API:**
331
+ ```typescript
332
+ const rlm = RLM.builder('gpt-4o-mini')
333
+ .apiKey(process.env.OPENAI_API_KEY!)
334
+ .withContextOverflow({ strategy: 'textrank', max_model_tokens: 32768 })
335
+ .build();
336
+ ```
337
+
338
+ **Strategy Comparison:**
339
+
340
+ | Strategy | API Calls | Speed | Quality | Best For |
341
+ |----------|-----------|-------|---------|----------|
342
+ | `mapreduce` | Many (parallel) | Medium | High | General-purpose, large documents |
343
+ | `truncate` | 0 | Fastest | Low | Quick-and-dirty, when beginning of doc matters |
344
+ | `chunked` | Many (sequential) | Slow | High | Detailed extraction from specific sections |
345
+ | `tfidf` | 0 | Fast | Medium | Fast first pass, keyword-rich documents |
346
+ | `textrank` | 0 | Fast | Medium-High | Documents with clear sentence structure |
347
+ | `refine` | Many (sequential) | Slow | Highest | When quality matters most, iterative refinement |
348
+
349
+ **Strategy Details:**
350
+
351
+ - **`mapreduce`** (default) - Splits context into chunks, summarizes each in parallel via LLM calls, then merges summaries. Good balance of quality and speed.
352
+ - **`truncate`** - Drops tokens from the end to fit the budget. Zero API calls, but loses information. Best when the beginning of the document is most important.
353
+ - **`chunked`** - Processes chunks sequentially, extracting relevant content from each. Higher quality than mapreduce for targeted extraction.
354
+ - **`tfidf`** - Pure Go, zero API calls. Uses TF-IDF scoring to select the most informative sentences. Preserves original document order. Great for a fast, no-cost first pass.
355
+ - **`textrank`** - Pure Go, zero API calls. Graph-based sentence ranking using PageRank over cosine-similarity of TF-IDF vectors. Better at identifying structurally important sentences than plain TF-IDF.
356
+ - **`refine`** - Sequential iterative refinement. Processes chunks one at a time, building and refining an answer progressively. Highest quality but slowest, as each chunk sees the accumulated context.
357
+
300
358
  ### Config Validation
301
359
 
302
360
  Catch configuration issues at construction time:
@@ -739,7 +797,10 @@ interface RLMConfig {
739
797
  temperature?: number; // Sampling temperature
740
798
  max_tokens?: number; // Maximum tokens in response
741
799
 
742
- // New in v5: Caching, retry, fallback
800
+ // Context overflow recovery
801
+ context_overflow?: ContextOverflowConfig;
802
+
803
+ // Caching, retry, fallback
743
804
  cache?: CacheConfig; // Cache configuration
744
805
  retry?: RetryConfig; // Retry configuration
745
806
  fallback?: FallbackConfig; // Fallback model configuration
@@ -768,6 +829,14 @@ interface FallbackConfig {
768
829
  strategy?: 'sequential'; // Fallback strategy
769
830
  }
770
831
 
832
+ interface ContextOverflowConfig {
833
+ enabled?: boolean; // Enable overflow recovery (default: true)
834
+ max_model_tokens?: number; // Override auto-detected model limit (0 = auto-detect)
835
+ strategy?: 'mapreduce' | 'truncate' | 'chunked' | 'tfidf' | 'textrank' | 'refine';
836
+ safety_margin?: number; // Fraction to reserve for overhead (default: 0.15)
837
+ max_reduction_attempts?: number; // Max reduction retries (default: 3)
838
+ }
839
+
771
840
  interface MetaAgentConfig {
772
841
  enabled: boolean; // Enable the meta-agent
773
842
  model?: string; // Model for query optimization (defaults to main model)
@@ -848,6 +917,7 @@ class RLMTimeoutError extends RLMError { elapsed; limit; }
848
917
  class RLMProviderError extends RLMError { statusCode; provider; }
849
918
  class RLMBinaryError extends RLMError { binaryPath; }
850
919
  class RLMConfigError extends RLMError { field; value; }
920
+ class RLMContextOverflowError extends RLMError { modelLimit; requestTokens; }
851
921
  class RLMSchemaError extends RLMError { path; constraint; }
852
922
  class RLMAbortError extends RLMError {}
853
923
  ```
package/bin/rlm-go CHANGED
Binary file
@@ -36,6 +36,18 @@ export interface TraceEvent {
36
36
  span_id?: string;
37
37
  parent_id?: string;
38
38
  }
39
+ export interface ContextOverflowConfig {
40
+ /** Enable automatic context overflow recovery (default: true) */
41
+ enabled?: boolean;
42
+ /** Override detected model token limit (0 = auto-detect from API errors) */
43
+ max_model_tokens?: number;
44
+ /** Strategy: 'mapreduce' (default), 'truncate', 'chunked', 'tfidf', 'textrank', or 'refine' */
45
+ strategy?: 'mapreduce' | 'truncate' | 'chunked' | 'tfidf' | 'textrank' | 'refine';
46
+ /** Fraction of token budget to reserve for prompts/overhead (default: 0.15) */
47
+ safety_margin?: number;
48
+ /** Maximum reduction attempts before giving up (default: 3) */
49
+ max_reduction_attempts?: number;
50
+ }
39
51
  export interface RLMConfig {
40
52
  recursive_model?: string;
41
53
  api_base?: string;
@@ -46,12 +58,14 @@ export interface RLMConfig {
46
58
  go_binary_path?: string;
47
59
  meta_agent?: MetaAgentConfig;
48
60
  observability?: ObservabilityConfig;
61
+ context_overflow?: ContextOverflowConfig;
49
62
  debug?: boolean;
50
63
  api_version?: string;
51
64
  timeout?: number;
52
65
  temperature?: number;
53
66
  max_tokens?: number;
54
67
  structured?: any;
68
+ [key: string]: any;
55
69
  }
56
70
  export interface FileStorageConfig {
57
71
  /** Storage type: 'local' or 's3' */
package/dist/errors.d.ts CHANGED
@@ -89,6 +89,16 @@ export declare class RLMSchemaError extends RLMError {
89
89
  constraint: string;
90
90
  });
91
91
  }
92
+ /** Thrown when the request exceeds the model's context window. */
93
+ export declare class RLMContextOverflowError extends RLMError {
94
+ readonly modelLimit: number;
95
+ readonly requestTokens: number;
96
+ constructor(opts: {
97
+ message: string;
98
+ modelLimit: number;
99
+ requestTokens: number;
100
+ });
101
+ }
92
102
  /** Thrown when an operation is aborted via AbortController. */
93
103
  export declare class RLMAbortError extends RLMError {
94
104
  constructor(message?: string);
package/dist/errors.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * - `suggestion` – human-readable remediation hint
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.RLMAbortError = exports.RLMSchemaError = exports.RLMConfigError = exports.RLMBinaryError = exports.RLMProviderError = exports.RLMTimeoutError = exports.RLMRateLimitError = exports.RLMValidationError = exports.RLMError = void 0;
11
+ exports.RLMAbortError = exports.RLMContextOverflowError = exports.RLMSchemaError = exports.RLMConfigError = exports.RLMBinaryError = exports.RLMProviderError = exports.RLMTimeoutError = exports.RLMRateLimitError = exports.RLMValidationError = exports.RLMError = void 0;
12
12
  exports.classifyError = classifyError;
13
13
  // ─── Base Error ──────────────────────────────────────────────────────────────
14
14
  class RLMError extends Error {
@@ -132,6 +132,22 @@ class RLMSchemaError extends RLMError {
132
132
  }
133
133
  }
134
134
  exports.RLMSchemaError = RLMSchemaError;
135
+ // ─── Context Overflow ─────────────────────────────────────────────────────────
136
+ /** Thrown when the request exceeds the model's context window. */
137
+ class RLMContextOverflowError extends RLMError {
138
+ constructor(opts) {
139
+ super(opts.message, {
140
+ code: 'CONTEXT_OVERFLOW',
141
+ retryable: true,
142
+ suggestion: `Request has ${opts.requestTokens} tokens but model limit is ${opts.modelLimit}. ` +
143
+ 'Enable context_overflow handling or reduce your context size.',
144
+ });
145
+ this.name = 'RLMContextOverflowError';
146
+ this.modelLimit = opts.modelLimit;
147
+ this.requestTokens = opts.requestTokens;
148
+ }
149
+ }
150
+ exports.RLMContextOverflowError = RLMContextOverflowError;
135
151
  // ─── Abort ───────────────────────────────────────────────────────────────────
136
152
  /** Thrown when an operation is aborted via AbortController. */
137
153
  class RLMAbortError extends RLMError {
@@ -153,6 +169,14 @@ exports.RLMAbortError = RLMAbortError;
153
169
  function classifyError(err, context) {
154
170
  var _a;
155
171
  const msg = typeof err === 'string' ? err : err.message;
172
+ // Context overflow
173
+ if (msg.toLowerCase().includes('maximum context length') || msg.toLowerCase().includes('context_length_exceeded') || msg.toLowerCase().includes('too many input tokens')) {
174
+ const limitMatch = msg.match(/maximum context length is (\d[\d,]*)/i);
175
+ const requestMatch = msg.match(/(?:has|requested) (\d[\d,]*)\s*(?:input )?tokens/i);
176
+ const modelLimit = limitMatch ? parseInt(limitMatch[1].replace(/,/g, ''), 10) : 0;
177
+ const requestTokens = requestMatch ? parseInt(requestMatch[1].replace(/,/g, ''), 10) : 0;
178
+ return new RLMContextOverflowError({ message: msg, modelLimit, requestTokens });
179
+ }
156
180
  // Rate limit
157
181
  if (msg.includes('429') || msg.toLowerCase().includes('rate limit') || msg.toLowerCase().includes('too many requests')) {
158
182
  const retryMatch = msg.match(/retry.after[:\s]+(\d+)/i);
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  export { RLM, RLMBuilder, RLMCompletionResult, RLMResultFormatter } from './rlm';
2
- export { RLMConfig, RLMResult, RLMStats, MetaAgentConfig, ObservabilityConfig, TraceEvent, FileStorageConfig } from './bridge-interface';
2
+ export { RLMConfig, RLMResult, RLMStats, MetaAgentConfig, ObservabilityConfig, TraceEvent, FileStorageConfig, ContextOverflowConfig } from './bridge-interface';
3
3
  export { BridgeType } from './bridge-factory';
4
4
  export { StructuredRLMResult, SubTask, CoordinatorConfig, SchemaDecomposition } from './structured-types';
5
5
  export { RLMExtendedConfig, ValidationResult, ValidationIssue, ValidationLevel, validateConfig, assertValidConfig } from './config';
6
- export { RLMError, RLMValidationError, RLMRateLimitError, RLMTimeoutError, RLMProviderError, RLMBinaryError, RLMConfigError, RLMSchemaError, RLMAbortError, classifyError, } from './errors';
6
+ export { RLMError, RLMValidationError, RLMRateLimitError, RLMTimeoutError, RLMProviderError, RLMBinaryError, RLMConfigError, RLMSchemaError, RLMContextOverflowError, RLMAbortError, classifyError, } from './errors';
7
7
  export { RLMStream, StreamOptions, StreamChunk, StreamChunkType, TextStreamChunk, PartialObjectStreamChunk, UsageStreamChunk, ErrorStreamChunk, DoneStreamChunk, createSimulatedStream, } from './streaming';
8
8
  export { RLMCache, CacheConfig, CacheStats, CacheProvider, MemoryCache, FileCache } from './cache';
9
9
  export { RetryConfig, FallbackConfig, withRetry, withFallback } from './retry';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildFileContext = exports.S3StorageError = exports.S3FileStorage = exports.LocalFileStorage = exports.FileContextBuilder = exports.RLMAgentCoordinator = exports.RLMEventEmitter = exports.withFallback = exports.withRetry = exports.FileCache = exports.MemoryCache = exports.RLMCache = exports.createSimulatedStream = exports.RLMStream = exports.classifyError = exports.RLMAbortError = exports.RLMSchemaError = exports.RLMConfigError = exports.RLMBinaryError = exports.RLMProviderError = exports.RLMTimeoutError = exports.RLMRateLimitError = exports.RLMValidationError = exports.RLMError = exports.assertValidConfig = exports.validateConfig = exports.RLMResultFormatter = exports.RLMBuilder = exports.RLM = void 0;
3
+ exports.buildFileContext = exports.S3StorageError = exports.S3FileStorage = exports.LocalFileStorage = exports.FileContextBuilder = exports.RLMAgentCoordinator = exports.RLMEventEmitter = exports.withFallback = exports.withRetry = exports.FileCache = exports.MemoryCache = exports.RLMCache = exports.createSimulatedStream = exports.RLMStream = exports.classifyError = exports.RLMAbortError = exports.RLMContextOverflowError = exports.RLMSchemaError = exports.RLMConfigError = exports.RLMBinaryError = exports.RLMProviderError = exports.RLMTimeoutError = exports.RLMRateLimitError = exports.RLMValidationError = exports.RLMError = exports.assertValidConfig = exports.validateConfig = exports.RLMResultFormatter = exports.RLMBuilder = exports.RLM = void 0;
4
4
  // ─── Core ────────────────────────────────────────────────────────────────────
5
5
  var rlm_1 = require("./rlm");
6
6
  Object.defineProperty(exports, "RLM", { enumerable: true, get: function () { return rlm_1.RLM; } });
@@ -19,6 +19,7 @@ Object.defineProperty(exports, "RLMProviderError", { enumerable: true, get: func
19
19
  Object.defineProperty(exports, "RLMBinaryError", { enumerable: true, get: function () { return errors_1.RLMBinaryError; } });
20
20
  Object.defineProperty(exports, "RLMConfigError", { enumerable: true, get: function () { return errors_1.RLMConfigError; } });
21
21
  Object.defineProperty(exports, "RLMSchemaError", { enumerable: true, get: function () { return errors_1.RLMSchemaError; } });
22
+ Object.defineProperty(exports, "RLMContextOverflowError", { enumerable: true, get: function () { return errors_1.RLMContextOverflowError; } });
22
23
  Object.defineProperty(exports, "RLMAbortError", { enumerable: true, get: function () { return errors_1.RLMAbortError; } });
23
24
  Object.defineProperty(exports, "classifyError", { enumerable: true, get: function () { return errors_1.classifyError; } });
24
25
  // ─── Streaming ───────────────────────────────────────────────────────────────
package/dist/rlm.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * console.log(result.result);
14
14
  * ```
15
15
  */
16
- import { RLMConfig, RLMResult, RLMStats, TraceEvent, FileStorageConfig } from './bridge-interface';
16
+ import { RLMConfig, RLMResult, RLMStats, TraceEvent, FileStorageConfig, ContextOverflowConfig } from './bridge-interface';
17
17
  import { BridgeType } from './bridge-factory';
18
18
  import { z } from 'zod';
19
19
  import { StructuredRLMResult } from './structured-types';
@@ -89,6 +89,8 @@ export declare class RLMBuilder {
89
89
  withFallback(models: string[]): this;
90
90
  /** Set the bridge type */
91
91
  bridge(type: BridgeType): this;
92
+ /** Configure context overflow recovery */
93
+ withContextOverflow(config?: ContextOverflowConfig): this;
92
94
  /** Set the Go binary path */
93
95
  binaryPath(path: string): this;
94
96
  /** Add LiteLLM passthrough parameters */
package/dist/rlm.js CHANGED
@@ -170,6 +170,11 @@ class RLMBuilder {
170
170
  this.bridgeType = type;
171
171
  return this;
172
172
  }
173
+ /** Configure context overflow recovery */
174
+ withContextOverflow(config) {
175
+ this.config.context_overflow = Object.assign({ enabled: true }, config);
176
+ return this;
177
+ }
173
178
  /** Set the Go binary path */
174
179
  binaryPath(path) {
175
180
  this.config.go_binary_path = path;
package/go/README.md CHANGED
@@ -159,6 +159,11 @@ All fields in `config` are optional and have defaults:
159
159
  | `max_iterations` | int | 30 | Maximum REPL iterations per call |
160
160
  | `temperature` | float | 0.7 | LLM temperature (0-2) |
161
161
  | `timeout` | int | 60 | HTTP timeout in seconds |
162
+ | `context_overflow.enabled` | bool | true | Enable context overflow recovery |
163
+ | `context_overflow.strategy` | string | `mapreduce` | Reduction strategy: mapreduce, truncate, chunked, tfidf, textrank, refine |
164
+ | `context_overflow.max_model_tokens` | int | 0 (auto) | Override detected model token limit |
165
+ | `context_overflow.safety_margin` | float | 0.15 | Fraction reserved for prompt overhead |
166
+ | `context_overflow.max_reduction_attempts` | int | 3 | Max retry attempts |
162
167
 
163
168
  Any other fields in `config` are passed as extra parameters to the LLM API.
164
169
 
@@ -258,7 +263,10 @@ rlm/ # Public package (importable)
258
263
  ├── prompt.go # System prompt builder
259
264
  ├── repl.go # JavaScript REPL (goja)
260
265
  ├── openai.go # OpenAI API client
261
- └── errors.go # Error types
266
+ ├── errors.go # Error types
267
+ ├── context_overflow.go # Context overflow detection + 6 reduction strategies
268
+ ├── tfidf.go # TF-IDF extractive compression (pure Go)
269
+ └── textrank.go # TextRank graph-based ranking with PageRank
262
270
  ```
263
271
 
264
272
  ## Error Handling