/** * Map-reduce chunked summarization orchestrator. * * See docs/scope-memos/v0.2.0-backend-abstraction-and-chunked-summarize.md * §5 for the design and §3.2 for the AbortSignal / timeout discipline this * implements. * * Flow: * 0. Validate options (OMCP_CHUNK_OVERLAP < OMCP_CHUNK_SIZE etc). * 1. Pre-flight abort check. * 2. Fast-path: byte-count estimate. If it clearly fits, verify with * actual countTokens; if it really fits, ONE single-call summarize * using FAST_PATH_SYSTEM. No chunking, no reduce. * 3. Chunking: RecursiveCharacterTextSplitter with backend tokenizer. * Hard-error if chunks > max_chunks (default 100). * 4. MAP: p-limit(concurrency) per chunk; each chunk gets a chained * AbortSignal (job ⊕ 50 s timeout). Queue-drain guard at chunk-fn * entry checks jobSignal.aborted and bails before any backend work. * Catch logic distinguishes job cancellation (re-throw) from local * timeout / error (substitute placeholder + chunksFailed++). * 5. REDUCE: pack summaries into ≤ 3 K-token buckets. If single * bucket fits, ONE reduce call → done. Else recurse with same * orchestration. Max depth 3; beyond that return partial. */ import type { LlmBackend } from '../llm/backend.js'; export declare const DEFAULT_CHUNK_SIZE = 2000; export declare const DEFAULT_CHUNK_OVERLAP = 200; export declare const DEFAULT_CONCURRENCY = 2; export declare const DEFAULT_MAX_CHUNKS = 100; /** * Multiply the user-configured chunk size by this factor before passing to * the splitter, to absorb proxy-tokenizer drift (js-tiktoken cl100k vs * Qwen / Llama tokenizers, up to ±15 %). Caller's apparent budget × 0.85 * keeps actual model usage under the budget when the proxy under-counts. */ export declare const TOKENIZER_SAFETY_FACTOR = 0.85; /** * Prompt-framing overhead that the map / reduce / fast-path system+template * prompts add on top of the user content. Used both for the actual num_ctx * sizing and for the fast-path budget check. */ export declare const PROMPT_OVERHEAD_TOKENS = 500; /** * Per-call soft timeout. Each backend.chat() inside MAP and REDUCE phases * is wrapped in `AbortSignal.any([jobSignal, AbortSignal.timeout(this)])`. * 50 s leaves ~10 s margin under Claude Code's hard ~60 s MCP request * timeout. */ export declare const PER_CALL_TIMEOUT_MS = 50000; /** * Generous timeout for the fast-path single call — it summarizes the whole * document in one shot, so it's allowed up to 4 × the per-chunk budget. * Still well under any sane MCP-client total timeout. */ export declare const FAST_PATH_TIMEOUT_MS = 200000; /** * Max prompt-token budget for a single REDUCE call. Sized so that * prompt-eval (≈ 6.4 s/1 K on oMLX Tier C) + reduce-output generation * (≈ 26.7 s for 400 tokens at thermal-throttled 15 t/s) finish under * PER_CALL_TIMEOUT_MS even on a loaded 16 GB Mac. See scope memo §5.2 * "Why the bucket budget shrinks across drafts". */ export declare const REDUCE_BUCKET_TOKENS = 3000; export declare const MAX_RECURSION_DEPTH = 3; /** MAP-phase output token budget — one fragment summary, 3-5 sentences. */ export declare const MAP_OUTPUT_BUDGET = 400; /** REDUCE-phase final-output token budget — 1-2 sentence lead + 3-6 bullets. */ export declare const REDUCE_OUTPUT_BUDGET = 800; export interface ChunkedSummarizeOptions { /** The full document text. */ source: string; /** Optional style hint forwarded to the REDUCE / fast-path prompt. */ style?: string; /** LLM backend used for both MAP and REDUCE phases. */ backend: LlmBackend; /** * Backend's per-call max input tokens (Tier C `num_ctx`, typically 32 K). * Used for the fast-path budget check. */ maxInputTokens: number; /** * Job-level AbortSignal — typically `extra.signal` from the MCP request * handler. The MCP SDK fires this on client cancellation / disconnect. */ signal: AbortSignal; /** Hard cap on chunk count. Default 100. */ maxChunks?: number; /** Override default chunk size in tokens (env: OMCP_CHUNK_SIZE). */ chunkSize?: number; /** Override default chunk overlap in tokens (env: OMCP_CHUNK_OVERLAP). */ chunkOverlap?: number; /** Override default fan-out concurrency (env: OMCP_CHUNK_CONCURRENCY). */ concurrency?: number; /** * Forwarded to every internal `backend.chat()` call (fast-path, MAP, * REDUCE, partial-REDUCE, recursive REDUCE). When `true`, suppresses * the model's reasoning trace (`/no_think` suffix). When `false`, * thinking is allowed. When `undefined`, the backend falls back to * env-var / its own default. */ disableThinking?: boolean; /** Optional progress callback wired to MCP `sendProgress`. */ onProgress?: (msg: string, current: number, total: number) => void | Promise; } export interface ChunkedSummarizeResult { /** The final summary text, trimmed. */ text: string; /** Number of chunks processed in the MAP phase. 1 means fast-path was taken. */ chunksProcessed: number; /** Number of REDUCE passes performed. 0 for fast-path; 1+ for normal flow. */ reduceDepth: number; /** True if recursion hit MAX_RECURSION_DEPTH and the result is incomplete. */ partial: boolean; /** Chunks whose MAP call timed out or errored; placeholders were substituted. */ chunksFailed: number; /** REDUCE-pass calls that timed out or errored; placeholders were substituted. */ reduceFailed: number; /** Total prompt tokens summed across every backend.chat() call this job made. */ promptTokens: number; /** Total completion tokens summed across every backend.chat() call this job made. */ completionTokens: number; } export declare function chunkedSummarize(opts: ChunkedSummarizeOptions): Promise; //# sourceMappingURL=map-reduce.d.ts.map