recursive-llm-ts 4.4.1 → 4.6.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.
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Streaming support for recursive-llm-ts.
3
+ *
4
+ * Provides `AsyncIterable`-based streaming for both text completions
5
+ * and structured output, with AbortController support.
6
+ */
7
+ export type StreamChunkType = 'text' | 'partial_object' | 'usage' | 'error' | 'done';
8
+ export interface StreamChunkBase {
9
+ type: StreamChunkType;
10
+ timestamp: number;
11
+ }
12
+ export interface TextStreamChunk extends StreamChunkBase {
13
+ type: 'text';
14
+ text: string;
15
+ }
16
+ export interface PartialObjectStreamChunk<T = unknown> extends StreamChunkBase {
17
+ type: 'partial_object';
18
+ object: Partial<T>;
19
+ /** JSON path of the field being populated */
20
+ path?: string;
21
+ }
22
+ export interface UsageStreamChunk extends StreamChunkBase {
23
+ type: 'usage';
24
+ usage: {
25
+ promptTokens: number;
26
+ completionTokens: number;
27
+ totalTokens: number;
28
+ };
29
+ }
30
+ export interface ErrorStreamChunk extends StreamChunkBase {
31
+ type: 'error';
32
+ error: Error;
33
+ }
34
+ export interface DoneStreamChunk extends StreamChunkBase {
35
+ type: 'done';
36
+ stats: {
37
+ llm_calls: number;
38
+ iterations: number;
39
+ depth: number;
40
+ };
41
+ }
42
+ export type StreamChunk<T = unknown> = TextStreamChunk | PartialObjectStreamChunk<T> | UsageStreamChunk | ErrorStreamChunk | DoneStreamChunk;
43
+ export interface StreamOptions {
44
+ /** AbortController signal to cancel the stream */
45
+ signal?: AbortSignal;
46
+ /** Called on each chunk (alternative to async iteration) */
47
+ onChunk?: (chunk: StreamChunk) => void;
48
+ }
49
+ /**
50
+ * An async iterable stream of completion chunks.
51
+ *
52
+ * Can be consumed with `for await...of` or by attaching an `onChunk` callback.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const stream = rlm.streamCompletion(query, context);
57
+ * let fullText = '';
58
+ * for await (const chunk of stream) {
59
+ * if (chunk.type === 'text') fullText += chunk.text;
60
+ * }
61
+ * ```
62
+ */
63
+ export declare class RLMStream<T = unknown> implements AsyncIterable<StreamChunk<T>> {
64
+ private chunks;
65
+ private resolvers;
66
+ private done;
67
+ private error;
68
+ private signal?;
69
+ private abortHandler?;
70
+ constructor(signal?: AbortSignal);
71
+ /** Push a chunk into the stream (called by the producer) */
72
+ push(chunk: StreamChunk<T>): void;
73
+ /** Signal an error on the stream */
74
+ pushError(err: Error): void;
75
+ /** Mark the stream as complete */
76
+ complete(stats: {
77
+ llm_calls: number;
78
+ iterations: number;
79
+ depth: number;
80
+ }): void;
81
+ private cleanup;
82
+ /** Collect all text chunks into a single string */
83
+ toText(): Promise<string>;
84
+ /** Collect the final structured object (for structured streaming) */
85
+ toObject(): Promise<T | undefined>;
86
+ [Symbol.asyncIterator](): AsyncIterator<StreamChunk<T>>;
87
+ }
88
+ /**
89
+ * Creates a simulated stream from a non-streaming completion result.
90
+ * Useful as a compatibility bridge until full streaming is implemented in the Go binary.
91
+ */
92
+ export declare function createSimulatedStream(text: string, stats: {
93
+ llm_calls: number;
94
+ iterations: number;
95
+ depth: number;
96
+ }, signal?: AbortSignal, chunkSize?: number): RLMStream;
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ /**
3
+ * Streaming support for recursive-llm-ts.
4
+ *
5
+ * Provides `AsyncIterable`-based streaming for both text completions
6
+ * and structured output, with AbortController support.
7
+ */
8
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
9
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
10
+ return new (P || (P = Promise))(function (resolve, reject) {
11
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
12
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
13
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
14
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
15
+ });
16
+ };
17
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
18
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
19
+ var m = o[Symbol.asyncIterator], i;
20
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
21
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
22
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.RLMStream = void 0;
26
+ exports.createSimulatedStream = createSimulatedStream;
27
+ const errors_1 = require("./errors");
28
+ // ─── RLM Stream ──────────────────────────────────────────────────────────────
29
+ /**
30
+ * An async iterable stream of completion chunks.
31
+ *
32
+ * Can be consumed with `for await...of` or by attaching an `onChunk` callback.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const stream = rlm.streamCompletion(query, context);
37
+ * let fullText = '';
38
+ * for await (const chunk of stream) {
39
+ * if (chunk.type === 'text') fullText += chunk.text;
40
+ * }
41
+ * ```
42
+ */
43
+ class RLMStream {
44
+ constructor(signal) {
45
+ this.chunks = [];
46
+ this.resolvers = [];
47
+ this.done = false;
48
+ this.error = null;
49
+ this.signal = signal;
50
+ if (signal) {
51
+ this.abortHandler = () => {
52
+ this.pushError(new errors_1.RLMAbortError());
53
+ };
54
+ signal.addEventListener('abort', this.abortHandler, { once: true });
55
+ }
56
+ }
57
+ /** Push a chunk into the stream (called by the producer) */
58
+ push(chunk) {
59
+ if (this.done)
60
+ return;
61
+ if (chunk.type === 'done') {
62
+ this.done = true;
63
+ }
64
+ if (this.resolvers.length > 0) {
65
+ const resolve = this.resolvers.shift();
66
+ resolve({ value: chunk, done: false });
67
+ if (chunk.type === 'done') {
68
+ // Resolve any remaining waiters
69
+ for (const r of this.resolvers) {
70
+ r({ value: undefined, done: true });
71
+ }
72
+ this.resolvers = [];
73
+ }
74
+ }
75
+ else {
76
+ this.chunks.push(chunk);
77
+ }
78
+ }
79
+ /** Signal an error on the stream */
80
+ pushError(err) {
81
+ if (this.done)
82
+ return;
83
+ this.error = err;
84
+ this.done = true;
85
+ // Reject any waiting resolvers
86
+ for (const resolve of this.resolvers) {
87
+ resolve({ value: { type: 'error', error: err, timestamp: Date.now() }, done: false });
88
+ }
89
+ this.resolvers = [];
90
+ this.cleanup();
91
+ }
92
+ /** Mark the stream as complete */
93
+ complete(stats) {
94
+ this.push({ type: 'done', stats, timestamp: Date.now() });
95
+ this.cleanup();
96
+ }
97
+ cleanup() {
98
+ if (this.signal && this.abortHandler) {
99
+ this.signal.removeEventListener('abort', this.abortHandler);
100
+ }
101
+ }
102
+ /** Collect all text chunks into a single string */
103
+ toText() {
104
+ return __awaiter(this, void 0, void 0, function* () {
105
+ var _a, e_1, _b, _c;
106
+ let text = '';
107
+ try {
108
+ for (var _d = true, _e = __asyncValues(this), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
109
+ _c = _f.value;
110
+ _d = false;
111
+ const chunk = _c;
112
+ if (chunk.type === 'text') {
113
+ text += chunk.text;
114
+ }
115
+ }
116
+ }
117
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
118
+ finally {
119
+ try {
120
+ if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
121
+ }
122
+ finally { if (e_1) throw e_1.error; }
123
+ }
124
+ return text;
125
+ });
126
+ }
127
+ /** Collect the final structured object (for structured streaming) */
128
+ toObject() {
129
+ return __awaiter(this, void 0, void 0, function* () {
130
+ var _a, e_2, _b, _c;
131
+ let latest;
132
+ try {
133
+ for (var _d = true, _e = __asyncValues(this), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
134
+ _c = _f.value;
135
+ _d = false;
136
+ const chunk = _c;
137
+ if (chunk.type === 'partial_object') {
138
+ latest = chunk.object;
139
+ }
140
+ }
141
+ }
142
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
143
+ finally {
144
+ try {
145
+ if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
146
+ }
147
+ finally { if (e_2) throw e_2.error; }
148
+ }
149
+ return latest;
150
+ });
151
+ }
152
+ [Symbol.asyncIterator]() {
153
+ return {
154
+ next: () => {
155
+ var _a;
156
+ // Check for abort
157
+ if (((_a = this.signal) === null || _a === void 0 ? void 0 : _a.aborted) && !this.error) {
158
+ this.pushError(new errors_1.RLMAbortError());
159
+ }
160
+ // Return buffered chunks first
161
+ if (this.chunks.length > 0) {
162
+ const chunk = this.chunks.shift();
163
+ if (chunk.type === 'done' || chunk.type === 'error') {
164
+ return Promise.resolve({ value: chunk, done: false }).then(r => {
165
+ return { value: undefined, done: true };
166
+ });
167
+ }
168
+ return Promise.resolve({ value: chunk, done: false });
169
+ }
170
+ // Stream is done
171
+ if (this.done) {
172
+ return Promise.resolve({ value: undefined, done: true });
173
+ }
174
+ // Wait for next chunk
175
+ return new Promise((resolve) => {
176
+ this.resolvers.push(resolve);
177
+ });
178
+ },
179
+ };
180
+ }
181
+ }
182
+ exports.RLMStream = RLMStream;
183
+ // ─── Stream Builder (for simulating streaming from non-streaming sources) ────
184
+ /**
185
+ * Creates a simulated stream from a non-streaming completion result.
186
+ * Useful as a compatibility bridge until full streaming is implemented in the Go binary.
187
+ */
188
+ function createSimulatedStream(text, stats, signal, chunkSize = 20) {
189
+ const stream = new RLMStream(signal);
190
+ // Simulate streaming by splitting text into chunks
191
+ (() => __awaiter(this, void 0, void 0, function* () {
192
+ try {
193
+ for (let i = 0; i < text.length; i += chunkSize) {
194
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
195
+ stream.pushError(new errors_1.RLMAbortError());
196
+ return;
197
+ }
198
+ const chunk = text.slice(i, i + chunkSize);
199
+ stream.push({ type: 'text', text: chunk, timestamp: Date.now() });
200
+ // Small yield to allow consumer to process
201
+ yield new Promise(resolve => setImmediate(resolve));
202
+ }
203
+ stream.complete(stats);
204
+ }
205
+ catch (err) {
206
+ stream.pushError(err instanceof Error ? err : new Error(String(err)));
207
+ }
208
+ }))();
209
+ return stream;
210
+ }
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