설치
npx -y @deepseek-ai/dsh plugin --profile web add github:TuringCorp-net/mosaic_compress이 설치 명령은 GitHub 저장소 주소에서 생성된 확인되지 않은 시작점입니다.
README
유지 관리자가 작성한 문서 스냅샷입니다.
MosaicCompress
Stateless dialogue compression based on natural forgetting curve.
LLM conversations grow linearly. MosaicCompress keeps them bounded — automatically, invisibly, and without the user ever knowing what a "Session" is.
How It Works
Your message array (R rounds, oldest → newest):
Round 1 ────→ Round (R-50) │ Heavy zone → ALL → 2 msgs
Round (R-49) → Round (R-30) │ Light zone → structural truncation, count unchanged
Round (R-29) ──→ Round R │ Raw zone → keep as-is
Steady state: constant message count — 2 + heavyStart × (messages per round), e.g. 102 messages for pure two-message rounds, whether at round 60 or round 15,000 (higher, but still constant, when tool-call rounds add messages). The compression ratio approaches 100%.
Philosophy: Alive Memory, Not a Handover Brief
The industry-standard answer to unbounded conversations is threshold summarization: when the window fills up, summarize everything into one brief and hand it to a fresh model. The conversation looks like it continues. But structurally it is amnesia followed by reading a diary:
- A switch moment. Memory breaks, then is rebuilt from a single summary call.
- Indiscriminate loss. The freshest instructions are paraphrased too — the exact part that must stay vivid. In our own A/B experiment the brief paraphrased the user's latest instruction and silently dropped an action item ("write the key points into MEMORY").
- Invisible loss. The next model cannot know what the brief omitted, so it cannot compensate.
MosaicCompress models the opposite: biological forgetting. A human does not remember round 3 of a 300-round conversation — they keep the lesson, the rules, the relationship. The algorithm reproduces that curve inside one message array:
recent 30 rounds → verbatim (vivid — what you are actually working on)
rounds 30–50 → structural truncation (reasoning/args/results trimmed, text kept)
rounds 50+ → one heavy pair: identity, environment, permissions, rules
No switch moment, no reset, no length limit. The heavy zone is semantic memory (rules that must never be forgotten); the middle is recent episodic memory; the raw zone is the vivid present. Loss is visible: the zone structure tells the model what it no longer knows, so it can fetch detail from shadowed storage on demand.
| Threshold summarization (industry) | MosaicCompress | |
|---|---|---|
| Metaphor | amnesia + diary | continuous vivid memory |
| Continuity | resets on every compaction | never resets |
| Loss | indiscriminate, invisible | graduated, visible |
| Recent turns | paraphrased at the worst moment | always verbatim |
| Purpose | portable handover brief | unbounded human–AI dialogue |
The two philosophies complement each other: a handover brief serves cold starts and long pauses; MosaicCompress serves staying in the conversation. Combined with a durable host-side store (e.g. a MEMORY.md file), human and AI keep talking under the same forgetting curve indefinitely. See docs/design.md §8/§10 for the formal position-is-age model behind this design.
Quick Start
npm install mosaic-compress
import { mosaicCompress, type MosaicConfig } from 'mosaic-compress';
const config: MosaicConfig = {
lightStart: 30, // keep 30 most recent rounds raw
lightWindow: 10, // compress every 10 rounds
heavyStart: 50, // rounds before this get heavy compression
heavyWindow: 10, // same cadence as light
callLLM: async (systemPrompt, userInput) => {
// Wire to OpenAI, Anthropic, or any LLM provider
const res = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userInput },
],
});
return res.choices[0].message.content ?? '';
},
};
// Call every turn — zero cost below threshold; structural light is millisecond-fast,
// Heavy folds take ~1-2s (one LLM summary call)
const compressed = await mosaicCompress(messages, config);
Features
- Stateless & repeatable — no session state; call it every turn, and the output can be fed back in as input
- Zero-cost below threshold — returns immediately if no compression is due
- Anti-jitter — compression only at configurable window boundaries
- LLM-agnostic — bring your own
callLLMfunction for Heavy (OpenAI, Anthropic, local models…); light runs zero-LLM - Tool-call safe — tool messages don't break round counting
- Graceful degradation — LLM failures don't block the conversation
API
mosaicCompress(messages, config)
| Param | Type | Description |
|---|---|---|
messages | Message[] | Full message array. System prompt at [0] is preserved as-is. |
config | MosaicConfig | Compression config (see below). |
| Returns | Promise<Message[]> | Compressed message array. |
MosaicConfig
| Field | Type | Default | Description |
|---|---|---|---|
lightStart | number | 30 | Most recent N rounds kept raw |
lightWindow | number | 10 | Anti-jitter: compress every N rounds |
heavyStart | number | 50 | Rounds beyond this → Heavy zone |
heavyWindow | number | 10 | Anti-jitter for heavy compression |
callLLM | (sys: string, user: string) => Promise<string> | optional | Your LLM call function — Heavy zone only; light is structural truncation. Omit it for light-only usage |
onCompress | (event: CompressEvent) => void | Promise<void> | optional | Hook after each compression; receives the original payload for host-side archiving |
DEFAULT_CONFIG
Prefer starting from the exported defaults and overriding only what you need:
import { mosaicCompress, DEFAULT_CONFIG, type MosaicConfig } from 'mosaic-compress';
const config: MosaicConfig = { ...DEFAULT_CONFIG, callLLM: async (sys, user) => { /* ... */ } };
All numeric fields must be positive integers (windows) / non-negative integers (starts),
and heavyStart must be greater than lightStart. Invalid configs throw a TypeError.
Message
interface Message {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string;
tool_call_id?: string;
tool_calls?: { id: string; type: 'function'; function: { name: string; arguments: string } }[];
}
Design
Read the full design document (English) or 中文设计文档.
Architecture Boundaries
MosaicCompress is intentionally stateless and lossy:
- Durable storage is the host's responsibility. The library compresses
the message array in place and never persists original payloads. Hosts
that need lossless history must archive the raw messages themselves —
through their own code, a database, or the host platform's persistence
layer (the
onCompresscallback hands every compressed-away original to the host for archiving). - Compression is lossy by design. Like any summarization approach, early details fade progressively. That is the point: the goal is an unbounded conversation, not lossless archival. If exact retrieval of early turns matters, pair this library with a persistence layer and re-read on demand.
Integration Notes
MosaicCompress is host-agnostic and works wherever a callLLM function
exists. Its primary integration reference is DeepSeek Harness (DSH)
(deepseek-ai/deepseek-harness
— everything is a plugin), whose task-level compaction / output retention /
spill complement this library's message-level compression (roles and order
preserved). A ready-to-use DSH plugin backend lives in
dsh-module/ (design docs in EN/中文).
Related:
- DeepSeek Harness — the host platform
- awesome-dsh-plugin — curated DSH plugin list
- awesome-deepseek-harness — DSH ecosystem list
- design docs (EN) / 设计文档(中文) — theory and empirical case study
See the Roadmap for upcoming work.
Benchmark
A deterministic simulation (zero LLM cost, reproducible) runs the real algorithm with a rule-based pseudo-LLM. Latest sweep (default parameters):
| Rounds | msgs in | msgs out | tokens in | tokens out | ratio | facts kept |
|---|---|---|---|---|---|---|
| 100 | 234 | 120 | 9,451 | 4,580 | 51.5% | 100% |
| 1,000 | 2,310 | 122 | 91,869 | 5,523 | 94.0% | 100% |
| 5,000 | 11,500 | 120 | 457,484 | 9,913 | 97.8% | 100% |
npm run bench # synthetic sweep: 100 / 500 / 1000 / 5000 rounds
npm run bench -- --file chat.json # analyze your own conversation file
The file mode accepts any JSON array of messages in the library's
Message shape and reports the compression ratio:
[{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}]
See benchmark/README.md for the full method, data
generation, findings, limitations, and the real-LLM spot check
(npm run bench:real — DeepSeek V4 Flash, <$0.01, 5/5 facts retained).
Development
# Run tests (zero LLM cost — uses mock responses)
npm test
# Type-check the whole project
npm run typecheck
# Or directly:
npx tsx tests/index.test.ts
License
MIT — TuringCorp | iAsk@turingcorp.net
프로젝트 파일 및 신호
표시된 항목은 디렉터리 스냅샷에서 감지된 공개 저장소 신호입니다.
저장소 정보
- 언어
- TypeScript
- 라이선스
- MIT
- 마지막 업데이트
- 2026. 8. 17. 오전 12:10
신중하게 설치하기
소스 코드, 권한, 수명 주기 스크립트, 의존성 및 네트워크 접근을 검토하고 신뢰하지 않는 플러그인은 격리 환경에서 테스트하세요.